Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

CaptainHat

macrumors newbie
Original poster
Mar 8, 2007
8
0
So I have a question about scoping and it's relation to pointer style C strings. Specifically, if you allocate a C string like:

char* test = "this is a C string";

since test is a char*, I'm guessing that this just sitting on the stack and that the string is also just a constant on the stack and that both of them will go out of scope when the function exits. I ran the following in XCode to test this as follows:

char* createCString();

int main (int argc, char * const argv[]) {
char* test;
test = createCString();
char* other_string = "this is some other string";
cout << test << endl;
return 0;
}

char* createCString(){
char* result = "this is a C style string.";
return result;
}

and somehow it does in fact output "this is a C style string." So I'm wondering what exactly the scope of this style of C string is?

Any help on this is appreciated b/c I'm kind of miffed. I tried reading the section on this in K&R but it didn't really help that much. Thanks for any help.
 
Upon further reflection, I believe it works like this:

The linker creates a header that defines the region where static values live. This region includes variables defined with "static" as well as all the string-type constants that would be created by the above code. When your app is loaded and run, the statics region is established in the appropriate manner. Where you say
Code:
char *test = "This is a C string";
what is generated is code that reserves a space on the stack for test and assigns it the address of the predefined string in the static data region.

Unix likes to keep code and data separate. This is a good™ thing.
 
Example code:

Code:
char *the_str(void)
{
   char *test = "Looney bin";
   return test;
}

main()
{
   char *a_str = "Hello";
   char *b_str = the_str();
}

The actual strings themselves are typically stored in a "constant strings" part of the program and are all accessible, assuming you know pointer. In the function "the_str()", the variable 'test' is indeed allocated on the stack and lost when the_str() returns. However, you returned the pointer to that constant string. The string itself is not stored on the stack, it's in the constant strings data area of the program.

As a matter of fact, you probably cannot modify the string. You can't say:

Code:
char *the_Str(void)
{
char *test = "Looney bin";
test[2] = 'x';
return test;
}

because the actual memory holding the string is compiled into the program as readonly memory. If you want an actual string stored on the stack you can modify you would have to create one.

Code:
char *the_str(void)
{
   char str[32];
   char *test = "Looney bin";

   strcpy(str, test);
   str[2] = 'x';
}

This is legal because you copied the constant string into an array on the stack. Notice you would *not* be allowed to return the pointer to 'str' from the function because that memory was allocated on the stack. It can't be used when you leave the function.
 
In C, C++ and Objective-C, a string literal like "This is a string" is actually the address of a static array of const char. Whether you write

char* p = "A string";

or

static const char secret_array [] = { 'A', ' ', 's', 't', 'r', 'i', 'n', 'g', 0 };
char* p = (char *) secret_array;

produces _exactly_ the same result.
 
Ultimately, though, you should not get into the habit of using string constants. If you need to use a constant, do it something like this

Code:
#define  [B]aCStyleString[/B]  "this is a C style string."
#define  [COLOR="Blue"]anotherString[/COLOR]  "this is some other string"

char* createCString();

int main (int argc, char * const argv[]) {
  char* test;
  test = createCString();
  char* other_string = [COLOR="Blue"]anotherString[/COLOR];
  cout << test << endl;
  return 0;
}

char* createCString(){
  char* result = [B]aCStyleString[/B];
  return result;
}

Using definitions in .h files makes projects much easier to manage as they inevitably get larger. In XCode, it also improves your workflow because if you have auto-complete, it finds and fills-in your definitions.
 
Scope and lifetime are two different things. The original post used the term "scope" when it was actually lifetime that was being tested.

http://en.wikipedia.org/wiki/C_syntax#Storage_duration_specifiers

http://frank.mtsu.edu/~csci117/manual/lab9/lab9.html

http://hubpages.com/hub/Scope-and-Lifetime-of-Variables-in-Cpp


Code:
#include <stdio.h>

static char *
str1()
{
  return "This will be static storage.";
}

static char *
str2()
{
  char str[] = "This will be auto storage (stack).";
  return str;  // produces a warning
}

static char *
str3()
{
  static char str[] = "This is static.";
  return str; 
}

int 
main( int argc, const char * argv[] ) 
{
  int autoOnStack[ 1 ] = { 0 };
  printf( "stack: %p\n", autoOnStack );

  char* str = "Outer str";
  printf( " str: %p %s\n", str, str );

  {
    char* str;  // scope & lifetime is limited to block

    str = str1();
    printf( "  str1: %p %s\n", str, str );  // %s is safe

    str = str2();
    printf( "  str2: %p\n", str );  // %s is dangerous: storage is gone

    str = str3();
    printf( "  str3: %p %s\n", str, str );  // %s is safe
  }

  printf( " str: %p %s\n", str, str );  // unaffected by str in block
}
 
Thanks everyone. This helped a lot. I (hopefully) think I understand what is going on now.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.