Thread: Pointers As 2D arrays

  1. #1
    Registered User TieFighter's Avatar
    Join Date
    Feb 2010
    Location
    La Crosse, WI
    Posts
    32

    Pointers As 2D arrays

    So, I wrote some code trying to teach myself how to use pointers as 2D arrays a little better, but I'm confused by the output. It should be really simple code - just trying to make a two dimensional array (first value holds pointers to strings, the second is the array of characters that compose the string), then fill it up with values stating what the array index is within each string. Anyway, code is as follows:

    Code:
    #include <strings.h>
    #include <stdlib.h>
    
    int arraysize = 6;
    char **myArr;
    
    int
    main(int argc, char **argv)
    {
      myArr = (char**)malloc(arraysize);
      int i, j;
      for(i = 0; i < arraysize; i++){
    	myArr[i] = "\n Slotfilled. %d\n", i;
    	printf(myArr[i]);
      }
    
      for(j = 0; j < arraysize; j++){
    	free(myArr[j]);
      }
      free(myArr);
      exit(0);
    }
    and what it printed out in the terminal was:

    Code:
     Slotfilled. 9400308
    
     Slotfilled. 9400308
    
     Slotfilled. 9400308
    
     Slotfilled. 9400308
    
     Slotfilled. 9400308
    
     Slotfilled. 9400308
    Segmentation fault
    So, why isn't it the numbers 0 through 5? int i isn't even a pointer, so it's not a pointer address, right? Why is it giving me a segmentation fault, am I trying to access something outside of the array?

    Also, the values entered into the array change for each time I run it, and is this thing leaking memory?
    Last edited by TieFighter; 03-04-2010 at 06:05 PM. Reason: Adding information (Also)

  2. #2
    Woof, woof! zacs7's Avatar
    Join Date
    Mar 2007
    Location
    Australia
    Posts
    3,459
    Code:
    myArr[i] = "\n Slotfilled. %d\n", i;
    Is really just (due to the comma operator)
    Code:
    myArr[i] = i;
    Your malloc is only 6 bytes long, integers are usually 4 bytes long. You can do the math if you move 2 positions along, and write an integer then you're going to go out of bounds.

    Why are you freeing each row? You've only got one malloc(), but you have 2 free()'s (one being in a loop).

    Perhaps you wanted:
    Code:
    #include <strings.h>
    #include <stdlib.h>
    
    
    
    int
    main(int argc, char **argv)
    {
      const int arraysize = 6;
      const int stringsize = 32;
      char **myArr = NULL;
      int i, j;
    
      myArr = malloc(arraysize * sizeof(char *));
    
      for(i = 0; i < arraysize; i++){
    	myArr[i] = malloc(stringsize);
    	sprintf(myArr[i], "\n Slotfilled. %d\n", i);
    	printf("%s", myArr[i]);
      }
    
      for(j = 0; j < arraysize; j++){
    	free(myArr[j]);
      }
      free(myArr);
      exit(0);
    }
    A few small things:
    * malloc() can fail, check its return value.
    * Don't put things at global scope that don't need to be.
    * Don't mix tabs and spaces for indenting.
    Last edited by zacs7; 03-04-2010 at 06:13 PM.

  3. #3
    Registered User
    Join Date
    Oct 2006
    Location
    Canada
    Posts
    1,243
    myArr = (char**)malloc(arraysize);
    You can, and I believe it is suggested to avoid casting (remove the "(char**)"). You should probably be do something like this instead
    Code:
    myArr = malloc( sizeof(char*) * arraysize)
    Code:
    myArr[i] = "\n Slotfilled. %d\n", i;
    You also should allocate memory for each item in the array. Then you should use "strcpy" or in this case you probably want "sprintf" to generate the string, to put into that index.

    Code:
    free(myArr[j]);
    This is an error because you're trying to "free" something that hasn't been dynamically allocated.

    The output values you're getting is more or less useless.
    Last edited by nadroj; 03-04-2010 at 06:39 PM. Reason: error

  4. #4
    Registered User TieFighter's Avatar
    Join Date
    Feb 2010
    Location
    La Crosse, WI
    Posts
    32
    alright I modified your suggestions a little bit based on some simple things that my compiler was whining about and also took the time to look up sprintf to see what the documentation was on it. I really didn't know that function existed before. Anyway, here's the code that I ended up with:

    Code:
    #include <strings.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int arraysize = 6;
    char **myArr;
    
    int
    main(int argc, char **argv)
    {
      myArr = (char**)malloc(arraysize * sizeof(char *));
      int i, j;
      for(i = 0; i < arraysize; i++){
    	myArr[i] = malloc(sizeof("\n Slotfilled. %d\n") + 1);
    	sprintf(myArr[i], "\n Slotfilled. %d\n", i);
    	printf("%s", myArr[i]);
        }
    
      for(j = 0; j < arraysize; j++){
    	free(myArr[j]);
      }
    
      free(myArr);
      exit(0);
    }
    First and foremost, thank you both very much for your help in debugging this so far. That said, the changes are made with why as follows:

    stdio.h is needed for sprintf
    I was guessing that's what you meant by typing "stringsize" because it's not a variable that you entered.
    As for the formatting suggestion and global variable stuff - isn't that really just semantics?

    the printf("%s", myArr[i]); line is necessary, but can anyone tell me why the "%s" part is necessary if myArr[i] is a char* pointer? char* is the definition of string to my understanding.

  5. #5
    Registered User
    Join Date
    Oct 2006
    Location
    Canada
    Posts
    1,243
    stdio.h is needed for sprintf
    I was guessing that's what you meant by typing "stringsize" because it's not a variable that you entered.
    I dont understand your second sentence here... is it related to the first? Anyways, the purpose of "stringsize" as recommended by zacs7 is simply to use it to declare a constant "maximum" size of each string (or each char* in the array). You must allocate some memory to the pointer before using it, so its good to define some constant value for this. If you later decide to change the length of the string (because you, say, changed the string message that each index is going to have), then you only have to change that one constant variable, opposed to changing it in many places if you didn't use a variable for it.

    As for the formatting suggestion and global variable stuff - isn't that really just semantics?
    Compilers dont care at all about the "formatting" of the program, and this is not the same thing as semantics. Compilers are extremely picky about syntax and semantics. For formatting, again, they don't care: you could write your program on one line if you wanted to. Global variables, however, is certainly something to be careful with. In general, you want to minimize the use of global variables. In "most cases" they aren't needed, just make the variables local to the function ("main").

    the printf("%s", myArr[i]); line is necessary, but can anyone tell me why the "%s" part is necessary if myArr[i] is a char* pointer? char* is the definition of string to my understanding.
    The "%s" is just the syntax of the arguments to "printf". "%s" means that the variable corresponding to this should be a "char*", opposed to, say "%d" for an integer. In standard C, there is no "string" type, but we usually use the type "char*" or "char[]" and call them "string(s of characters)".

  6. #6
    Registered User TieFighter's Avatar
    Join Date
    Feb 2010
    Location
    La Crosse, WI
    Posts
    32
    Quote Originally Posted by nadroj View Post
    I dont understand your second sentence here... is it related to the first? Anyways, the purpose of "stringsize" as recommended by zacs7 is simply to use it to declare a constant "maximum" size of each string (or each char* in the array). You must allocate some memory to the pointer before using it, so its good to define some constant value for this. If you later decide to change the length of the string (because you, say, changed the string message that each index is going to have), then you only have to change that one constant variable, opposed to changing it in many places if you didn't use a variable for it.
    The second sentence is unrelated to the first. It's the second part of the code in red.

    Isn't declaring as a constant a waste of memory space if I tell it to declare a larger size than the string actually is?

    Quote Originally Posted by nadroj View Post
    Compilers dont care at all about the "formatting" of the program, and this is not the same thing as semantics. Compilers are extremely picky about syntax and semantics. For formatting, again, they don't care: you could write your program on one line if you wanted to. Global variables, however, is certainly something to be careful with. In general, you want to minimize the use of global variables. In "most cases" they aren't needed, just make the variables local to the function ("main").
    Although it is true that formatting is not usually a part of runtime semantics or static semantics it is a part of the more broad definition of semantics - how something is written. I'm in a course teaching structure of compilers currently, and that's actually the reason I'm forced to learn C after using other languages - primarily Java, but also Scheme, Prolog, Perl, ASM, P/L SQL, and Haskell. I did have a course in C++ once, but when we got to ADTs in that language I was just completely lost between constructors, destructors and pointers.

    I guess I could have thrown the Array in a struct, and used #define for the length of the array (or better yet - have it as one of the argument inputs to main() to redefine it whenever I run it), but I was just putting out some quick and dirty code to test out.


    Quote Originally Posted by nadroj View Post
    The "%s" is just the syntax of the arguments to "printf". "%s" means that the variable corresponding to this should be a "char*", opposed to, say "%d" for an integer. In standard C, there is no "string" type, but we usually use the type "char*" or "char[]" and call them "string(s of characters)".
    I understand %s %d %c %f. I'm just not sure why this function needs it rather than a type cast like (char*)myArr[i]. Is there a non-formatted print function that wouldn't need "%s"?

  7. #7
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by TieFighter View Post
    I understand %s %d %c %f. I'm just not sure why this function needs it rather than a type cast like (char*)myArr[i]. Is there a non-formatted print function that wouldn't need "%s"?
    This one is easy. It's a security issue.
    You don't know manner of kinds of nonsense might be stored inside a string, especially if it came from the user. And printf accepts a format specifier as the first string, so if format specifiers are hidden inside the string, all kinds of hell can break loose. If you instead pass a fixed format specifier and pass the string as what printf should output, you will be safe.

    Also, there is an additional problem in the code.
    sizeof("\n Slotfilled. %d\n") + 1
    This will not do what you think it does.
    sizeof will count %d as 2 characters, while an integer can take up as much as 9 character (if it's 4 bytes, it could reach up to around 4*10^9, which is at least 9 characters), so there is a real possibility that you are creating a heap overflow, another security issue.
    To be on the safe side, allocate enough to hold the largest integer.

    If you thought C++ was confusing, then welcome to C, where you will be even more confused!
    ...Maybe.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  8. #8
    Registered User TieFighter's Avatar
    Join Date
    Feb 2010
    Location
    La Crosse, WI
    Posts
    32
    Code:
    #include <strings.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    char ** createArray (int arraysize) {
      char **arrToCreate;
      arrToCreate = (char**)malloc(arraysize * sizeof(char *));
      printf("CREATED ARRAY AT MEM LOCATION: %d\n", (int)arrToCreate);
      int i;
      for(i = 0; i < arraysize; i++){
        arrToCreate[i] = malloc(sizeof("Slotfilled. ") + sizeof(int) + 1);
        sprintf(arrToCreate[i], "Slotfilled. %d", i);
        printf("\n%s\n", arrToCreate[i]);
      }
      return arrToCreate;
    }
    
    void destroyArray(int arraysize, char** arrToDestroy) {
      int j;
      for(j = 0; j < arraysize; j++){
        printf("\nremoving \"%s\"\n", arrToDestroy[j]);
        free(arrToDestroy[j]);
      }
      printf("\nDESTROYING ARRAY AT MEM LOCATION: %d\n", (int)arrToDestroy);
      free(arrToDestroy);
    }
    
    int main(int argc, char **argv) {
      char **myArr;
      int arraysize = 3;
      myArr = createArray(arraysize);
      destroyArray(arraysize, myArr);
      exit(0);
    }
    Better?

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Last time I checked, sizeof(int) is 4 or 8 bytes usually, which != 9 bytes required for the longest int. This code will crash and burn, behaving differently on different platforms!
    Also use %p when printing memory locations. Don't forget to cast to void*.
    An integer can not always fit a memory address! Certainly not on a 64-bit Windows system!
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Registered User TieFighter's Avatar
    Join Date
    Feb 2010
    Location
    La Crosse, WI
    Posts
    32
    so, in short, what you're saying is:
    1. sizeof(int) doesn't actually give me the size of an integer
    2. nobody is updating the C programming language for 64 bit machines so we have to cover that ground as programmers
    ?

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    No, what I am implying is that:
    1) sizeof(int) gives the size of the integer data type that can hold up around 4 * 10^9. However, as you should know, when you use a string, you use one byte per character. So if you have 4 000 000 000, it takes up 10 characters, hence 10 bytes (for the char data type). Remember that just because it takes up 4 bytes in integer form doesn't mean it does in char type, because they are separate types.
    2) Absolutely not. You are simply doing something strictly defined by the standard, but it will not always give you the results you want. If you do it right, it will work everywhere, 32-bit or 64-bit or 16-bit.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Registered User TieFighter's Avatar
    Join Date
    Feb 2010
    Location
    La Crosse, WI
    Posts
    32
    okay, so it doesn't store the integer separate from the string, rather it embeds it, so if I did boundary value analysis on this code it would crash and burn near the upper bounds. Cool.

    When reading or writing to memory locations in any programming language, shouldn't the size of the memory location needed for a data type be determined by the compiler built for that OS? I don't intend to transfer a binary file from my Ubuntu 32x laptop to any other OS, but if I did I'd expect it not to work unless I recompiled the file on that machine or if it were another Unix 32x machine. If I was looking for transportability between machines I'd just write it in Java, so it would run on the JVM on any machine.

  13. #13
    Registered User
    Join Date
    Jan 2010
    Posts
    412
    Quote Originally Posted by TieFighter View Post
    When reading or writing to memory locations in any programming language, shouldn't the size of the memory location needed for a data type be determined by the compiler built for that OS?
    The compiler does know that an int is 4 bytes (for x86-32, other cpus might use different sizes), but it doesn't know how many bytes a string representation of that int is.
    It knows that a char is 1 byte, but it doesn't know how many chars to use.
    (int)12345 and (int)1 both take up the same space in memory, but "12345" takes up more space than "1".

  14. #14
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Plus C is a low-level language in a high-level language sense. It doesn't handle these things for you. You're responsible for making it work.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  15. #15
    Registered User TieFighter's Avatar
    Join Date
    Feb 2010
    Location
    La Crosse, WI
    Posts
    32
    Good point - C was created specifically to design the Unix OS. Why anybody would design anything other than a compiler or OS with it is beyond me.

    I'm still struggling with how to implement another function in this program. I apologize if I come across as argumentative. I'm frustrated with learning this language - and with myself because I can't pick it up as fast as I need to. Any help and suggestions you give are still greatly appreciated.

    Code:
    bool enterName(char* name, char** arrayOfNames, int numSlots) {
      int m;
      for(m = 0; arrayOfNames[m] != NULL && m < numSlots; m++){
        printf("\nLooking through: %s\n", arrayOfNames[n]);
      }
      if(m == (numSlots - 1)) {
        printf("\nArray is full!  Stop trying to add stuff!  %s won't fit!\n", name);
        return false;
      } else {
        printf("\nFound a free spot to store: %s\n", name);
        //arrayOfNames[m] = malloc(sizeof(name) + 1);
        memmove(arrayOfNames[m], name, (sizeof(name) + 1) );
        return true;
      }
      return false; // If you made it this far there was probably no name entered.
    }
    I'm trying to put this in the previous code with the methods - I did make modifications as suggested by Elysia. I get a compile error for the red line above in my newly attempted function.
    Code:
    24: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘enterName’
    Last edited by TieFighter; 03-09-2010 at 07:14 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. pointers to arrays
    By rakeshkool27 in forum C Programming
    Replies: 1
    Last Post: 01-24-2010, 07:28 AM
  2. 2d array of object pointers
    By Potterd64 in forum C++ Programming
    Replies: 17
    Last Post: 07-20-2006, 01:27 PM
  3. Pointers to Multidimensional Arrays
    By kidburla in forum C Programming
    Replies: 10
    Last Post: 10-29-2005, 10:45 PM
  4. returning 2D arrays
    By ... in forum C++ Programming
    Replies: 2
    Last Post: 09-02-2003, 12:28 PM
  5. Initialising 2D and 3D arrays
    By fry in forum C++ Programming
    Replies: 5
    Last Post: 08-01-2002, 04:34 AM