I’m doing Zed Shaw’s Learn C The Hard Way course. On exercise 11, in the extra-credit question #2, he askes:
Make these loops count backward by using i– to start at argc and count down to 0. You may have to do
some math to make the array indexes work right.Use a while loop to copy the values from argv into states.
I try:
#include <stdio.h>
int main(int argc, const char *argv[]){
int i = argc - 1;
while(i >= 0){
printf("arg %d: %s\n", i, argv[i]);
i--;
}
char *states[] = {
"California", "Oregon",
"Washington", "Texas"
};
int num_states = 4;
i = num_states - 1;
while( i >= 0){
printf( "state %d, %s\n", i, states[i]);
i--;
}
i = 0;
while(i < argc && i < num_states){
int j = 0;
while( (states[i][j++] = argv[i][j++]) != '\0' ){
i++;
}
states[i][j] = '\0';
}
i = num_states - 1;
while( i >= 0){
printf( "state %d, %s\n", i, states[i]);
i--;
}
return 0;
}
I get a Segmentation Fault. I understand that you can’t copy arrays in C, that they are const pointer or something similar (or so I read). That’s why I try to copy character by character:
while(i < argc && i < num_states){
int j = 0;
while( (states[i][j++] = argv[i][j++]) != '\0' ){
i++;
}
states[i][j] = '\0';
}
Yet it doesn’t work. How should I do this? The compiler gives me this warning when I compile:
$ make ex11
cc -Wall -g ex11.c -o ex11
ex11.c: In function ‘main’:
ex11.c:26:28: warning: operation on ‘j’ may be undefined [-Wsequence-point]
I don’t get why it says that j is undefined. valgrind says this:
$ valgrind ./ex11 this is a test
==4539== Memcheck, a memory error detector
==4539== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==4539== Using Valgrind-3.8.0 and LibVEX; rerun with -h for copyright info
==4539== Command: ./ex12 this is a test
==4539==
arg 4: test
arg 3: a
arg 2: is
arg 1: this
arg 0: ./ex11
state 3, Texas
state 2, Washington
state 1, Oregon
state 0, California
==4539==
==4539== Process terminating with default action of signal 11 (SIGSEGV)
==4539== Bad permissions for mapped region at address 0x400720
==4539== at 0x4005F1: main (ex11.c:26)
==4539==
==4539== HEAP SUMMARY:
==4539== in use at exit: 0 bytes in 0 blocks
==4539== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==4539==
==4539== All heap blocks were freed -- no leaks are possible
==4539==
==4539== For counts of detected and suppressed errors, rerun with: -v
==4539== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Segmentation fault
[EDIT 1]
A switched my code for this
int i = 0;
while( i < argc && i < num_states ){
states[i] = argv[i];
i++;
}
It does work, but the compiler gives me a warning. Also, I realized after posting this question that my previous:
while( (states[i][j++] = argv[i][j++]) != '\0' ){
i++;
}
Is just plain wrong. Because the j++ is executed twice per loop, and that i++ should be outside that loop in the outer loop. And also as mentioned in the comment below, that the byte-by-byte copy that I try to do using array of arrays doesn’t work because I actually have array of pointers.
That said, is there a way I could do this without having the compiler’s warning?
One problem (the one that causes the SEGV) is that while
statesis an array ofchar *, you’re initializing it with a bunch of string literals (which areconst char *). So you should at least be getting a warning on that line. This comes to bite you when you try to write into the string constant (which is in read-only memory).Now if you really want to copy the strings from
argvtostatesone byte at a time (rather than just copying pointers — egstates[i] = argv[i]), you need to make sure thatstates[i][j]is writable. You could do that by makingstatesan array of arrays of chars, instead of an array of pointers to chars:Of course, these are fixed size arrays, so you need to worry about overflowing them, but you already had the problem of overflowing the states array itself.