tags:

views:

78

answers:

5

I practiced an array of strings with no initial values.

Attempt 1

#include <stdio.h>

char *array[] = {};
int main(int argc, char *argv[]) {
    array[0]="Hello";
    array[1]="World";

    char **i = array;
    while (*i) {
        printf("%d %s\n", i, *i);
        i++;
    }
}

$ gcc array_of_strings.c && ./a.out

6293704 Hello
6293712 World

It works fine.

Attempt 2

I thought I could move array pointer inside main function.

#include <stdio.h>

int main(int argc, char *argv[]) {
    char *array[] = {};
    array[0]="Hello";
    array[1]="World";

    char **i = array;
    while (*i) {
        printf("%d %s\n", i, *i);
        i++;
    }
}

$ gcc array_of_strings.c && ./a.out

-1899140568 (j͎?
-1899140560 World
-1899140552 ???%Y
-1899140544 1?I??^H??H???PTI???@
-1899140536 d?͎?
Segmentation fault

Huh, why it is not working? It causes "Segmentation fault" with ugly output. Could somebody explain why I should not do this way?

+1  A: 

Doing it on the second attempt declare the array on the stack and you then override arguments passed and at some point the return address at the end of the function.

The fact that it works in the first attempt is purely coincidental. You're still overriding memory you shouldn't but it currently does no harm.

Eric Fortin
+8  A: 

You allocate an array with zero elements and then add two pointers to it. This writes outside of the array and causes a buffer overflow.

Incidentally, this overwrites unused memory if the array is allocated globally, but it overwrites the stack when it is allocated within main().

Sjoerd
+1: That's the biggest problem. Another thing is that you don't have anything like `array[2]=NULL` so the `while (*i)` won't stop reliably where you want.
torak
+2  A: 

In each of these cases, you are allocating an empty array, and then trying to insert items into it. C doesn't do any sort of resizing of arrays; if you insert items into an array beyond its length, it will just start overwriting any other data that might happen to come after the array. In your first case, when the array is a global, you manage to get lucky and apparently aren't breaking anything by writing past the end of the array, and furthermore are lucky that there is a null value just past the two that you insert, so your loop terminates at an appropriate place. In your second case, you happen to be overwriting your stack, which is used for storing local variables, passing arguments, return values, and return locations between functions. Thus, writing, and later reading, past the end of your array, is causing you to write all over your stack, and read random meaningless values.

Brian Campbell
A: 

You should not do it in both ways. In the first case, you were just lucky that you didn't overwrite some process-critical memory, in the second case you smashed the stack. Both cases might crash randomly because you are writing to memory that you did not reserve.

char *array[] = {};

This does reserve memory for zero entries, but with [0] = ... you're writing an element to a position for which you didn't allocate memory. You should read up about how to 1) define static arrays or 2) dynamically allocate arrays.

AndiDog
+2  A: 

Two problems.

  1. You aren't allocating space for the array items. With your empty initializer list you are allocating an empty array. When you write to array[0] and array[1] you are writing to memory you do not own.

  2. You are getting lucky when you allocate the array globally. Global (aka statically-allocated) memory blocks tend to be filled with zeros. This is good for you because your while loop depends on their being a NULL pointer at the end of the array.

    When you allocate on the stack and access memory past the end of the array you will get whatever happens to already be on the stack, which can be any arbitrary garbage. Your while (*i) loop doesn't get the NULL pointer it expects so it will keep on reading the garbage data until it finds some zeros that look like a NULL pointer.

To fix #1, give an explicit length to the array. To fix #2, you must explicitly add a NULL pointer to the end of the array.

char *array[3];
array[0]="Hello";
array[1]="World";
array[2]=NULL;

Also, for what it's worth, pointers aren't guaranteed to be the same size as ints. It is better to use %p to print pointers rather than %d.

printf("%p %s\n", i, *i);
John Kugelman
Not sure how you've come up with this solution. There are no spaces for the characters. It should be `char array[3][6]` and `strcpy()` should be used instead of plain `=` operator.
Donotalo
@Donotalo - `array` is an array of `char*`. It is fine to assign string literals to those array slots. No need for extra memory allocation or `strcpy`.
John Kugelman
i'm confused. how does that is supposed to work? any link to standard?
Donotalo