Thread: Defensive programming?

  1. #1
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964

    Defensive programming?

    I've made this small application to get some practize using dynamic memory allocation with new/delete and to get better at defensive programming. All it does is loop through an array of ints and prompt the user for input, it then prints them all on one line. (The size of the array is determined by user input from the command line)

    Code:
    /* A simple program to demonstrate the use of dynamic memory allocation and defensive programming */
    #include <iostream>
    
    int main(int argc, char *argv[])
    {
    	int n = 0;			/* Variable to hold number from commandline */
    	int i = 0;			 /* Index variable for program loop */
    	int * Input_ptr;	    /* Pointer for memory allocation */
    	
    	/* Check to see if program was run with correct number of parameters */
    	if(argc != 2)
    	{
    		std::cerr << "Invalid number of parameters." << std::endl;
    		exit(1);
    	}
    	
    	/* Check to see whether or not the first parameter is a number greater than 0 */
    	if(!atoi(argv[1]))
    	{
    		std::cerr << "Wrong parameters, please input a number greater than 0." << std::endl;
    		exit(1);
    	}
    	
    	/* Allocate memory according to input from commandline */
    	n = atoi(argv[1]);
    	Input_ptr = new (std::nothrow) int[n];
    	
    	/* Check for memory overflow */
    	if(!Input_ptr)
    	{
    		std::cerr << "Memory overflow error." << std::endl;
    		exit(1);
    	}
    	
    	/* Main program loop */
    	for(i; i < n; i++)
    	{
    		std::cout << "Enter value " << (i+1) << ": ";
    		std::cin >> Input_ptr[i];
    	}
    	
    	/* Print values to screen */
    	std::cout << "You have entered: ";
    	for(i = 0; i < n; i++)
    	{
    		std::cout << Input_ptr[i];
    		
    		/* Check to see if last letter has been printed, otherwise, print comma */
    		if(!(i == n-1))
    		{
    			std::cout << ", ";
    		}
    	}
    	
    	/* End program */
    	delete[] Input_ptr;
    	return 0;
    }
    I'm not quite done with the defensive programming part, i still need to evaluate the input the user enters into the array, if for example someone enters letters rather than numbers, the loop goes through the entire array and ignores the "std::cin". Then when the program prints out the values in the next loop i get some obscure value, i'm fairly sure this is because cin enters a failstate mode, due to invalid input, but i'm not sure how to fix this?

    The other problem i have is with the 3rd if statement, the one where i check for memory overflow. If i run my program with a command like this one: "main.exe 130982123098", i _should_ be getting the overflow error message, but instead it just enters the loop and lets me start inputting values, and i'm not patient enough to keep inputting numbers just to see how far i will get before i run out of memory. The computer im compiling on has 1024 Mb of memory, even coupled with the swap file, this should still result in an overflow error, amirite?

    My guess is that it has something to do with the fact that i'm overflowing the int i'm storing the values for the loop in. (int n to be exact) Should i rather use something else, like a long int? What if the user then overflows that? What should i do to solve this one?

    Oh, and how is my program style-wise, the only reason i'm using "std::nothrow" is because i still haven't gotten around to exceptions yet, so if that is bad practice or something, just bear with me.

    Thanks alot for helping

    Edit: The boards seems to be screwing up my indentation...
    Last edited by Neo1; 08-09-2007 at 03:22 PM.

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> i'm fairly sure this is because cin enters a failstate mode, due to invalid input, but i'm not sure how to fix this?
    Check the return value of cin. A common technique that you can adapt for your needs is:
    Code:
    while (!(std::cin >> input))
    {
      // Clear failbit so cin can take input again.
      std::cin.clear();
    
      // Ignore bad data in stream from previous user input.
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    
      // indicate error to user and re-prompt.
    }
    >> If i run my program with a command like this one: "main.exe 130982123098", i _should_ be getting the overflow error message
    Just output n after you get it from atoi and see what it returns, it might return an amount smaller than what you input that is too small to cause memory allocation to fail. You could use long, but on many current platforms that is the same size as int. To force an overflow, you can create an array of a different type. For example, perhaps you could make a struct with an int and a large array. You can adjust the array size according to how big you need it to see your overflow. When you try to dynamically allocate a bunch of instances of that struct, it will be much easier to overflow.

  3. #3
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964
    Quote Originally Posted by Daved View Post
    >> i'm fairly sure this is because cin enters a failstate mode, due to invalid input, but i'm not sure how to fix this?
    Check the return value of cin. A common technique that you can adapt for your needs is:
    Code:
    while (!(std::cin >> input))
    {
      // Clear failbit so cin can take input again.
      std::cin.clear();
    
      // Ignore bad data in stream from previous user input.
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    
      // indicate error to user and re-prompt.
    }
    Thanks alot, i've tried changing my code for the input to this:
    Code:
    /* Main program loop */
    for(i; i < n; i++)
    {
    	std::cout << "Enter value " << (i+1) << ": ";
    	std::cin >> Input_ptr[i];
    	
    	/* Validate user input */
    	while (!(std::cin >> Input_ptr[i]))
    	{
    		// Clear failbit so cin can take input again.
    		std::cin.clear();
    
    		// Ignore bad data in stream from previous user input.
    		std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    
    		std::cerr << "An error occured, please input value " << (i+1) << " again: ";
    		std::cin >> Input_ptr[i];
    	}
    		
    }
    But with this addition, the program hangs when i enter the first value, shouldn't the above be working?


    Quote Originally Posted by Daved View Post
    >> If i run my program with a command like this one: "main.exe 130982123098", i _should_ be getting the overflow error message
    Just output n after you get it from atoi and see what it returns, it might return an amount smaller than what you input that is too small to cause memory allocation to fail. You could use long, but on many current platforms that is the same size as int. To force an overflow, you can create an array of a different type. For example, perhaps you could make a struct with an int and a large array. You can adjust the array size according to how big you need it to see your overflow. When you try to dynamically allocate a bunch of instances of that struct, it will be much easier to overflow.
    I'm not quite sure i understand what you mean, you wan't me to make a structure containing a large array, to force overflow when the user input is too high? That seems very wasteful to me, just to make a huge big array of nothing to force overflow? Or have i misunderstood you?

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> shouldn't the above be working?
    Not quite. You call cin twice the first time through. There is no need for the std::cin >> Input_ptr[i]; before the loop, since the one inside the while loop control will get data from the user.

    >> That seems very wasteful to me, just to make a huge big array of nothing to force overflow?
    My understanding was that you were trying to test your memory overflow check to make sure it works. If your program cannot cause a memory allocation error because you are not allocating enough memory, then you could allocate more memory to verify that your check works. If you don't want to verify that your check works, then just leave it as is.

    This is of course assuming that inputting the largest possible value for an int will not cause an overflow. Making the int unsigned will allow you a larger number, but I'm not sure if atoi can handle that. You might have to switch to using a stringstream to convert the argument string to an unsigned number to allow for larger inputs.

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by Neo1 View Post
    I'm not quite sure i understand what you mean, you wan't me to make a structure containing a large array, to force overflow when the user input is too high? That seems very wasteful to me, just to make a huge big array of nothing to force overflow? Or have i misunderstood you?
    I think Daved's idea was to "enlarge the array for your test-purposes". Say you have an array that is 1M, then you can exhaust 2GB of RAM with a value of 2000 or so, whilst you'll need 536870912 (or something a few tenthousands below that) to exhaust 2GB with integers.

    But also, I'm pretty sure the number you showed was larger than a 32-bit number. This means that atoi() will overflow - I don't know EXACTLY what it does under those circumstances, and it may be that you get whatever the number is " & (1 << 32-1)", which means that it's not necessarily a very large number (I haven't looked at what your number becomes under those circumstances).

    --
    Mats

  6. #6
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964
    Quote Originally Posted by Daved View Post
    >> shouldn't the above be working?
    Not quite. You call cin twice the first time through. There is no need for the std::cin >> Input_ptr[i]; before the loop, since the one inside the while loop control will get data from the user.
    D'oh! Obviously! Thanks a bunch, i also had to remove the one after the std::cerr, but it's working like a charm now!


    Quote Originally Posted by Daved View Post
    >> That seems very wasteful to me, just to make a huge big array of nothing to force overflow?
    My understanding was that you were trying to test your memory overflow check to make sure it works. If your program cannot cause a memory allocation error because you are not allocating enough memory, then you could allocate more memory to verify that your check works. If you don't want to verify that your check works, then just leave it as is.
    Yes, that was my plan, but if the int that holds the inputted value (Or atoi() for that matter), overflows before i run out of memory, the check becomes redundant, so i need to find a way to hold the value inputted that won't overflow, or atleast give an error if it does.

    Quote Originally Posted by Daved View Post
    This is of course assuming that inputting the largest possible value for an int will not cause an overflow. Making the int unsigned will allow you a larger number, but I'm not sure if atoi can handle that. You might have to switch to using a stringstream to convert the argument string to an unsigned number to allow for larger inputs.
    But does stringstream work for c-style strings? Argv is a pointer to a c-style string, and i was under the impression that stringstreams are for std::strings?

    But regardless of what stringstream can do, using an unsigned int really doesn't make the program completely idiot-proof, it's still very possible to overflow the int, isn't it possible to make this program completely bulletproof, after all, it's not a very complicated program

  7. #7
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964
    Quote Originally Posted by matsp View Post
    I think Daved's idea was to "enlarge the array for your test-purposes". Say you have an array that is 1M, then you can exhaust 2GB of RAM with a value of 2000 or so, whilst you'll need 536870912 (or something a few tenthousands below that) to exhaust 2GB with integers.

    But also, I'm pretty sure the number you showed was larger than a 32-bit number. This means that atoi() will overflow - I don't know EXACTLY what it does under those circumstances, and it may be that you get whatever the number is " & (1 << 32-1)", which means that it's not necessarily a very large number (I haven't looked at what your number becomes under those circumstances).

    --
    Mats
    It seems that both atoi() and the int im using to hold value is overflowing, so instead of doing the memory check, wouldn't it be simpler to just check the inputted value to see if it's bigger than what atoi() and the integer can hold? (But then again, how would i do that before using atoi() to convert it to a number rather than chars? A bit tricky, so should i use stringstreams?)

  8. #8
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    You can try using "strtol", but all it does when it detects an overflow is to return "MAX_LONG" (1 << 31), and it doesn't put anything in the endptr either to indicate the overflow.

    But at least if someone enters 1234A, endptr will point to "A".

    --
    Mats

  9. #9
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> does stringstream work for c-style strings?
    You can use a stringstream with C-style strings, since a C style string can be converted to and from a C++ string.

    As far as your overflow check, there are two possible "overflow" error conditions.

    The first is the command line string that might overflow the datatype you used to hold it (int, unsigned int, long or unsigned long, whichever you choose). I'm not sure how to identify with atoi whether the value overflows the int, it might not be simple. Just look up atoi in your reference and it should say what it does (Edit - the value is undefined, so don't use atoi). Same with stringstreams.

    Assuming you have been given a valid value for that size of your array, you then have to check your memory allocation to see if you ran out of memory. With nothrow new checking for null is correct, so there's nothing you need to do with your code to make it more bulletproof in that sense. If you want to test to make sure it works, then you have to find a way to allocate enough memory that will cause the allocation error. That's why I suggested the struct, to test your memory allocation failure check.

  10. #10
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    "strtol" will set errno to "ERANGE" (Result too large) if you enter a number beyond the limits. It also returns LONG_MAX (or LONG_MIN if the number is negative) in this case.

    You probably want to check that the value isn't negative before you try to create an array of that many - it may not treat it as a negative number at that point, which means that you get a HUGE number...

    --
    Mats

  11. #11
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Code:
    /* Check to see whether or not the first parameter is a number greater than 0 */
    	if(!atoi(argv[1]))
    	{
    		std::cerr << "Wrong parameters, please input a number greater than 0." << std::endl;
    		exit(1);
    	}
    	
    	/* Allocate memory according to input from commandline */
    	n = atoi(argv[1]);
    You can probably do "strtol" once for these two instances of atoi. Then check if its greater than zero, rather than not zero.

    --
    Mats

  12. #12
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964
    Quote Originally Posted by Daved View Post
    >> does stringstream work for c-style strings?
    You can use a stringstream with C-style strings, since a C style string can be converted to and from a C++ string.

    As far as your overflow check, there are two possible "overflow" error conditions.

    The first is the command line string that might overflow the datatype you used to hold it (int, unsigned int, long or unsigned long, whichever you choose). I'm not sure how to identify with atoi whether the value overflows the int, it might not be simple. Just look up atoi in your reference and it should say what it does (Edit - the value is undefined, so don't use atoi). Same with stringstreams.
    Well, from what i could gather, atoi() does no overflow checking, actually, most of this hits i got from google, encouraged people to use strtol() for that same reason, but i'm not really familiar with this function. strtol() uses long int's, right? So i would have to make int n a long int instead, can i still use it for the new operator then? Or will new only accept normal int's?

    Assuming you have been given a valid value for that size of your array, you then have to check your memory allocation to see if you ran out of memory. With nothrow new checking for null is correct, so there's nothing you need to do with your code to make it more bulletproof in that sense. If you want to test to make sure it works, then you have to find a way to allocate enough memory that will cause the allocation error. That's why I suggested the struct, to test your memory allocation failure check.
    Wouldn't it be easier to just set int n to a very large value before i call new? Wouldn't this generate the same result?

  13. #13
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> Wouldn't it be easier to just set int n to a very large value before i call new? Wouldn't this generate the same result?

    Maybe, but it sounded like you tried the largest int possible and it still didn't cause a memory allocation error. If you didn't try the largest int possible, try it. Chances are that will be 2147483647. Or switch to unsigned and try 4294967295. If neither of those cause a memory allocation error, then you cannot test it without changing your code.

  14. #14
    Internet Superhero
    Join Date
    Sep 2006
    Location
    Denmark
    Posts
    964
    Quote Originally Posted by matsp View Post
    Code:
    /* Check to see whether or not the first parameter is a number greater than 0 */
    	if(!atoi(argv[1]))
    	{
    		std::cerr << "Wrong parameters, please input a number greater than 0." << std::endl;
    		exit(1);
    	}
    	
    	/* Allocate memory according to input from commandline */
    	n = atoi(argv[1]);
    You can probably do "strtol" once for these two instances of atoi. Then check if its greater than zero, rather than not zero.

    --
    Mats
    I tried this code instead of my orignal using atoi():

    Code:
    n = strtol(argv, NULL, 10);
    /* Check to see whether or not the first parameter is a number greater than 0 */
    if(!n)
    {
    		std::cerr << "Wrong parameters, please input a number greater than 0." << std::endl;
    		exit(1);
    }
    	
    /* Allocate memory according to input from commandline */
    Input_ptr = new (std::nothrow) int[n];
    But the strtol() function won't accept argv and the first parameter, it want's a "const char*"? What should i do about that, sorry if i'm beginning to wear you guys out, i'm just not that good with this stuff yet, and it's getting late here..

  15. #15
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    You can definitely use a long for indexing, so therefore you must be able to use long for "new" too - or it wouldn't work to create larger than 2GB char arrays on a 64-bit machine, and that would kind of hinder things a bit. On a 32-bit machine, long and int are the same size anyways, so shouldn't make any difference to anything.

    --
    Mats

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. help please.. linkedlists/classes
    By swayp in forum C++ Programming
    Replies: 10
    Last Post: 01-18-2005, 05:14 PM
  2. Segmentation fault with structs and char pointers
    By Keybone in forum C++ Programming
    Replies: 20
    Last Post: 01-17-2004, 01:36 PM
  3. Bowling for Columbine
    By Silvercord in forum A Brief History of Cprogramming.com
    Replies: 97
    Last Post: 09-09-2003, 07:48 PM