07 Feb, 2012, JohnnyStarr wrote in the 1st comment:
Votes: 0
I'm trying to knuckle down and master C strings once and for all.
I figured I would go ahead and open a thread because I will have plenty of questions I'm sure.

My first is why this doesn't work:

#include <stdio.h>

int main ()
{
char *orig = "Hey you guys.";
char *str;

str = &orig;

while(*str++) {
if (*str == 'y')
*str = '@';
}

puts(orig);

return 0;
}

// OUTPUT => "Hey you guys."
// Not "he@ @ou gu@s." as expected.


This is odd because by assigning str = &orig;, I assumed that would be the same memory address.
What am I missing here?
07 Feb, 2012, Vigud wrote in the 2nd comment:
Votes: 0
C99, google N1256.pdf said:
EXAMPLE 8
The declaration
char s[] = "abc", t[3] = "abc";
defines plain char array objects s and t whose elements are initialized with character string literals.
This declaration is identical to
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
The contents of the arrays are modifiable. On the other hand, the declaration
char *p = "abc";
defines p with type pointer to char and initializes it to point to an object with type array of char
with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to
modify the contents of the array, the behavior is undefined.
07 Feb, 2012, Runter wrote in the 3rd comment:
Votes: 0
Quote
What am I missing here?


That you can't modify strings initialized with a literal. The pointers do point to the same place. You can prove it by printing the value of the pointer rather than dereferencing it.

Try doing strdup("hey you guys") and you'll find that works I believe.
08 Feb, 2012, plamzi wrote in the 4th comment:
Votes: 0
Runter said:
Quote
What am I missing here?


That you can't modify strings initialized with a literal. The pointers do point to the same place. You can prove it by printing the value of the pointer rather than dereferencing it.

Try doing strdup("hey you guys") and you'll find that works I believe.


Yeah, strdup will work because it allocates before it duplicates. It's equivalent to initializing a char array of fixed size and content, and then pointing to it (all that in "p = strdup("abc");). On the flipside, it's easy to forget that strdup (unlike all other str* funcs) does that and use it in ways that leak memory.

Welcome to the "wonderful" world of C "string" processing.
08 Feb, 2012, Scandum wrote in the 5th comment:
Votes: 0
JohnnyStarr said:
This is odd because by assigning str = &orig;, I assumed that would be the same memory address.
What am I missing here?

I haven't double checked this, but as far as I know str = orig and str = &orig[] are valid. This because orig is a pointer and doesn't actually hold any actual data like an int would.

So orig holds a pointer to the character array, while &orig points to the pointer's physical address, which you typically have little use for.

With the while loop you're using you're also skipping the first character. You'd probably want to use:
while(*str) {
if (*str == 'y')
*str++ = '@';
else
str++
}
08 Feb, 2012, Runter wrote in the 6th comment:
Votes: 0
Quote
This because orig is a pointer and doesn't actually hold any actual data like an int would.


This is incorrect. It holds actual data just like other variables would. It takes memory for both the pointer (data) and the additional data the pointer may reference.

Quote
So orig holds a pointer to the character array, while &orig points to the pointer's physical address, which you typically have little use for.


This statement complicates the matter more than it needs to be. orig holds a value. &orig is a computed value. If you somehow knew the address space you could set it yourself without needing to use &orig. That's obviously not very useful for most people, since you don't know, but the point is that they can be thought of like any other primitive datatype. Which goes back to the previous point. It's just data. Dereferencing that data replaces in the context you accessed it with whatever data at that address. It's a language construction…the dereferencing magic, that is. Not really the pointers. They're just types designated to hold that value for your convenience. Understanding this can be important later on, especially if you end up doing something where you reallocate memory for something and its address changes… that would necessarily require you being able to know what references it. Without understanding that, it can cause some C growing pains as you develop more complex software.

void main() {
printf("%s", *&"test");
}


And there's no reason to not use for statement here. It's cleaner and straight forward. My own personal preference is to only use while if I know for a fact it's a simple conditional check. The pointer arithmetic stuff I would avoid while on.
void main() {
char *str = "helloy world";


for(;*str; ++str)
printf(str);
}
08 Feb, 2012, David Haley wrote in the 7th comment:
Votes: 0
In fact, if you compile this with stricter settings, it will give you a warning that you're assigning a const char* to a char*.

Sometimes, depending on the compiler and where it puts these string literals, you'll even get a segfault for attempting to modify a string in the data segment.
08 Feb, 2012, Runter wrote in the 8th comment:
Votes: 0
David Haley said:
Sometimes, depending on the compiler and where it puts these string literals, you'll even get a segfault for attempting to modify a string in the data segment.


Yeah, I was going to mention that…I always seem to get segfaults when I do that.
08 Feb, 2012, David Haley wrote in the 9th comment:
Votes: 0
You can avoid the segfault with a reverse pointer hash literal grid.
08 Feb, 2012, Tyche wrote in the 10th comment:
Votes: 0
1) char * foo = "bar"; is allocated in string literal pool which isn't modifiable (although gcc has a -fwrite-strings option which makes it legal to modify literal strings)
2) char foo[] = "bar" is more interesting in that "bar" is actually allocated in two places, the string literal pool and when the program runs it copies "bar"
from the literal pool to the stack and assigns that address to foo. — this might be implementation dependent(!).

char * baz;
3) baz = foo; copies the address contained in foo to baz; which will be the literal pool address of "bar" in 1), or the stack address of "bar" in 2)
4) baz = &foo; copies the address of foo to baz which is also on the stack. *baz will contain the address of "bar", not baz
08 Feb, 2012, Kaz wrote in the 11th comment:
Votes: 0
Also, you're assigning a char** to a char*. I have no idea how your compiler hasn't thrown a wobbly about that.
08 Feb, 2012, JohnnyStarr wrote in the 12th comment:
Votes: 0
Thanks for the input guys…

Here's my next question:
#include <stdio.h>
char *map[50][50];

int main(void) {

char exit[100];
char *desc;
strcpy(exit, "East");

map[0][0] = exit;

desc = map[0][0];

strcat(desc, " - To Tom Bombidil's house.");

printf("%s <- updated.", map[0][0]);

return 0;

}

// OUTPUT => "East - To Tom Bombidil's house. <- updated."


By making a 2d array of type char*, I am hoping to be able to implement an ASCII mapper.

My concern is this seems too easy. I didn't realize that you could even have an array of char*, so I am hesitant
to put to much effort into this without knowing for sure what it will yield. What are the hazards here? Should I
be allocating the map with malloc() or calloc()?

** I am currently developing with codepad.org (I'm at work), which accounts for the lack of compiler warnings.
08 Feb, 2012, Davion wrote in the 13th comment:
Votes: 0
JohnnyStarr said:
Thanks for the input guys…

Here's my next question:


By making a 2d array of type char*, I am hoping to be able to implement an ASCII mapper.

My concern is this seems too easy. I didn't realize that you could even have an array of char*, so I am hesitant
to put to much effort into this without knowing for sure what it will yield. What are the hazards here? Should I
be allocating the map with malloc() or calloc()?

** I am currently developing with codepad.org (I'm at work), which accounts for the lack of compiler warnings.


You can use malloc, calloc and if you reuse it, realloc. You also want to make sure you use free on it.

You want to keep in mind what you're making.
char * map[50][50]; //is an array of pointers to chars.


You'd of course want to make sure you initialize everything to zero first (use memset or somesuch).
When you use it. It's as simple as

if( map[x][y] )
free(map[x][y]);
map[x][y] = strdup("{d|{x");


Keep in mind, the hardest aspect about c char arrays is the pointer arithmetic and the memory juggling.
08 Feb, 2012, Vigud wrote in the 14th comment:
Votes: 0
Runter said:
Quote
What am I missing here?
That you can't modify strings initialized with a literal.
If we're allowed to make such simplifications, then I dare to say that it is actually OK to modify strings initialized with a string literal. Let me quote the C99 standard again:
Vigud said:
C99, google N1256.pdf said:
EXAMPLE 8
The declaration
char s[] = "abc", t[3] = "abc";
defines plain char array objects s and t whose elements are initialized with character string literals.
This declaration is identical to
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
The contents of the arrays are modifiable. On the other hand, the declaration
char *p = "abc";
defines p with type pointer to char and initializes it to point to an object with type array of char
with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to
modify the contents of the array, the behavior is undefined.


But more strictly speaking, you're incorrect because you can't even initialize string. C is more primitive language than you think and it doesn't even let you read or write strings. That's because C strings are not objects; string is defined as "a sequence of characters that ends with NULL terminator". So in the world of C, string is just interpretation of given data (in most cases, stored in array of chars). Knowing this, you probably understand why some time ago I made a claim that it may be safe to read data from beyond a string. Consider the following example:

{
char a[] = "these are strings\0more strings\0yet more strings";
char *b = "these are strings\0more strings\0yet more strings";

printf("%s\n", a);
printf("%s\n", &a[35]);
printf("%s\n", &b[35]);

return;
}

In both cases I'm reading data from beyond the string "these are strings", yet, it's perfectly safe. Please note that it doesn't matter if it's an array of chars (a) or a pointer to unnamed array of chars (b). By the way, you should be able to see why I used the plural form of word "string".
08 Feb, 2012, KaVir wrote in the 15th comment:
Votes: 0
Scandum said:
This because orig is a pointer and doesn't actually hold any actual data like an int would.

They hold a memory address, which is usually a 32 bit integer.

Scandum said:
So orig holds a pointer to the character array, while &orig points to the pointer's physical address, which you typically have little use for.

&orig returns the address of a variable which contains another memory address. This can be a useful technique in certain string handling functions, because C technically only supports pass-by-value.

Runter said:
void main() {

Dude, please.

Vigud said:
If we're allowed to make such simplifications, then I dare to say that it is actually OK to modify strings initialized with a string literal. Let me quote the C99 standard again:

You've misread it. The literal is being copied into an array, and then the array can be modified. Tyche explained this in his second example in post #10.

Vigud said:
In both cases I'm reading data from beyond the string "these are strings", yet, it's perfectly safe. Please note that it doesn't matter if it's an array of chars (a) or a pointer to unnamed array of chars (b).

They are both safe to read from, but only (a) is safe to write to.
08 Feb, 2012, David Haley wrote in the 16th comment:
Votes: 0
Yes, what KaVir said. Assigning into an array is not at all the same as assigning to a pointer.
08 Feb, 2012, Vigud wrote in the 17th comment:
Votes: 0
KaVir said:
Vigud said:
If we're allowed to make such simplifications, then I dare to say that it is actually OK to modify strings initialized with a string literal. Let me quote the C99 standard again:

You've misread it. The literal is being copied into an array, and then the array can be modified. Tyche explained this in his second example in post #10.
I assumed Runter was talking about being able to modify the contents of the array, and that's what I called simplification. Because if that's what he meant, then he was wrong.
KaVir said:
Vigud said:
In both cases I'm reading data from beyond the string "these are strings", yet, it's perfectly safe. Please note that it doesn't matter if it's an array of chars (a) or a pointer to unnamed array of chars (b).

They are both safe to read from, but only (a) is safe to write to.
I didn't state otherwise, so I don't know why you're bringing this into discussion.
08 Feb, 2012, David Haley wrote in the 18th comment:
Votes: 0
Vigud said:
Because if that's what he meant, then he was wrong.

I'm not sure why you think that's what he meant. He didn't even use the word "array".

"So I don't know why you're bringing this into discussion." :wink:
08 Feb, 2012, Vigud wrote in the 19th comment:
Votes: 0
Because he said something about initializing a string and that's what can't be done in C, so I had to guess.
08 Feb, 2012, David Haley wrote in the 20th comment:
Votes: 0
I think that if you're going to tell somebody they're wrong, you should make sure you're not guessing and assuming vague things about what they actually mean. It's more polite. :smile:
0.0/39