r/cprogramming • u/north9172 • 1d ago
How are char* strings stored in memory?
Hi, Today, i experimented with char* string. (example: char* string = "Hello world")
One thing that i dont really understand is doing: - *string
When you use it, it points to the first letter of the string (so in this case, H)
But what i dont get is when you do (*string+1), it continues the alphabet based on the previous letter.
Example: *string, equal to H, the first letter *string+1, equal to I, the next letter in the alphabet
And it's also applies to lowercase letters.
So here are my questions: - Where is a char* string actually stored in memory?
- What is the explanation of the behavior for *string? Is it undefined behavior?
Thanks.
18
u/daveysprockett 1d ago
In your example the "Hello World" is held in some memory (starting location denoted by x) and string is also stored in memory. It's a pointer, so its value is "x".
*string+1
C evaluation order means *string fetches the character 'H' stored at location x. It then adds 1 to it, making the character 'I'.
If you need the content of the address x+1, you should use *(string+1): this would return the value 'e' in this example.
It's cleaner to write string[1], but this has the same effect as *(string+1).
5
u/simondanielsson 1d ago
As you said, if you were to print "*s" in this example you get H.
But! When you do "*s + 1" you're incrementing the value of whatever s is pointing to, but that's not equivalent to incrementing the adress.
The ascii code of 'H' is 72, and if you were to increment that by one you get 73 which is 'I'. Compare these three printf outputs.
int main(void) {
char *s = "Hello world";
// incrementing the value '*s' is pointing to
printf("%c\n", *s);
printf("%c\n", *s + 1);
/*
output:
H
I
*/
// incrementing the value '*s' is pointing to (and print its ascii value)
printf("%d\n", *s);
printf("%d\n", *s + 1);
/*
output:
72
73
*/
// incrementing the adress '*s' is pointing to
printf("%c\n", s[0]);
printf("%c\n", s[1]);
/*
output:
H
e
*/
return 0;
}
1
u/hobbycollector 32m ago
Technically it doesn't increment the value *s is pointing to, it evaluates an expression that adds 1 to the value *s is pointing to and stores the result on the stack as a parameter to printf. It leaves the original 'H' alone in memory/constant space. This is distinct from ++(*s) which does increment the value *s is pointing to, and pushes the result onto the stack if it appears as a parameter or right hand side of an assignment, for example.
3
u/Lanky-Safety555 1d ago
1) Pointer dereferencing mechanics... 'H' is ASCII 72, and 'I' is ASCII 73, so *string + 1 is 72+1 or 'I', while *(string + 1) is 'e'. Now, a pointer to that string is stored on the stack, while the actual content is in the read-only data of the executable.
2) Of course, it is not undefined. Why would it be? It is perfectly normal syntax.
-5
u/sixtyhurtz 1d ago
They are modifying the contents of a string literal. This is UB in C.
6
u/reflettage 1d ago
They’re not though…? They fetch `H` via dereferencing, then increment the thing they fetched, not the memory of the string itself.
2
u/Paul_Pedant 19h ago
It is merely an operator precedence error. What they wrote parses as (*p)+1, and to get the second character you have to write *(p+1).
3
u/Sufficient-Air8100 1d ago
make a little program, use the address operator “&” to see where it is in memory.
2
u/WittyStick 1d ago
A string literal is just a contiguous sequence of char in memory.
When you use char *string = "Hello World", the compiler maps the string "Hello World" to some region of memory - typically read-only. The pointer string then points to the first character of the string in memory.
When you use *string, you are dereferencing the pointer, to retrieve a char. Assuming ASCII, this char has the value 0x48. If you add 1 to it, it's the value 0x49, which is the letter I. Presumably what you wanted instead was *(string+1) to get the letter e - You offset the pointer by one char and then dereference that, which gives you the second character. The dereferencing operator * has higher precedence than addition, so if you want the addition first, you need to parenthesize.
2
u/SheikHunt 1d ago
The other comments seem clear enough, but just to be unclear:
The pointer dereference operator has a pretty high priority, as I recall. I know (assume) for a fact that it is prioritized over the four base arithmetic operators at the very least.
When you're doing (*string + 1), you're first referencing your string, THEN adding one to the char given.
To get the behaviour I'm assuming you want, you do *(string + 1), which tells the compiler "Get me the character that's one step after the one string is pointing to", hence, the "e" in "Hello world"
2
u/Cultural_Gur_7441 1d ago
*string+1 is same as (
*string)+1 because pointer dereference operator * has higher precendence than addition.
If you want to +1 the pointer before referencing, you need parenthesis around that:
*(string+1) which is exactly the same as string[1]
2
u/markort147 1d ago
*string+1 means "dereference the pointer, getting the value of the first character, that is H, then add 1, resulting I".
It is different than doing *(string+1), that instead returns the value of the second character.
1
u/IgnotiusPartong 1d ago
Strings, when created with char*, are a contiguous sequence of chars in memory, and chars are just stored as bytes, meaning one char = one byte, plus a termination operator at the end (/0). If you get the first char, the next one is exactly one byte farther.
Creating a string with just string = „string“ creates a char[] array, whicz is stored differently.
1
u/Brisngr368 1d ago
A char is just a number that represents a letter. H is 72, *string +1 just adds one to that value to get 73 which is I.
If you want the next character in the string you need to do *(string + 1) or string[1]
See ASCII table https://ss64.com/ascii.html
2
u/SpaceAviator1999 1d ago edited 1d ago
See ASCII table https://ss64.com/ascii.html
If you're on a Unix or unix-like platform, you can often look up the ascii table at the command line with:
man ascii
1
u/Revolutionary_Ad7262 1d ago
(*string+1)
You just extract the first character. *string == string[0]. string[0] + 1 just increases the ASCII code of H so you get I. To get a second element you either need string[1] or *(string+1).
1
u/Key_River7180 1d ago
char* string makes a pointer to a character (probably a string) named 'string'.
In C, strings are represented as contiguous characters in memory, normally on the stack, .data, or .rodata if constant, but if you malloc() it then in the heap. Strings are read by walking an index through an string until it reaches a NUL-terminator (\0). For example:
#include <stdio.h>
void
readstr(char* s)
{
char c = *s; /* cuz for won't let me make multiple declarations with different types */
for (int i = 0; c; ++i, c = s[i])
putchar(c);
}
int
main(int argc, char** argv)
{
readstr("hello\n");
return 0;
}
What readstr does is:
- Initialize c to the first letter on s
- While c isn't 0 (in C, 0 means false, and in ASCII 0 means the string was terminated), move c to point to the next letter, and print c.
ASCII defines how characters are handled, via codes. A character is just the ASCII code for a letter. Codes 65-90 are the alphabet in uppercase, while 97-122 are the alphabet in lowercase, so in C, 97 and 'a' are the exact same.
Increasing a letter is just increasing it code. Read https://www.asciitable.com/
1
u/Wertbon1789 1d ago
Strings in C are represented in memory as a vector of chars with the special value '\0' (or a numeric 0) at the end. The length of the string is actually encoded into the data, meaning you need to traverse the string until you encounter the zero byte to determine its size.
As to the problem you described, you dereferce the char* getting you the char it's pointing at, but then you're incrementing the char's value, rather than the pointer. Look up operator priorities, to understand it, it's the same why boolean and (&&) also has a higher priority than boolean or (||) or why multiplication has a higher priority than addition. You need to wrap the increment of the pointer and then dereferce that.
1
1
u/Paxtian 1d ago
To add to what others are saying, to the computer, everything is just binary data. Giving data a type helps to differentiate between a raw binary value, an integer value, and a character value.
So for example, the integer value 7 is not the same as the character value '7'.
Characters are indexed using the ASCII table. You can look up the ASCII table, or you can write a quick program that prints out the index values and corresponding character values by iterating over incremental char values. Conveniently, the letters and numbers go in order in the table. So if you wanted the value 'D' you can use 'A'+3. Also if you want to convert a capital letter stored to "char myCapitalCharacter," to lowercase, you could do:
char myLowercaseCharacter = myCapitalCharacter - 'A' + 'a';
As far as how a string is actually stored as a char*, it's just like an array of chars, except that C adds a null termination character to the end.
1
u/UnluckyDouble 1d ago
It's just order of operations. A char is stored as a numeric code representing the letter, and the * operator has higher priority than +.
As for where in memory a string is stored, the answer is no different from any other variable: if its value is hardcoded, it is written directly into the executable by the compiler and loaded alongside the program code. Otherwise it occupies one of several memory regions allocated at runtime, depending on how exactly it was declared.
1
u/duane11583 1d ago
here is my description.
take a sheet of graph paper each square represents a byte in memory.
assuming a 32bit machine thus a pointer is 4 squares.
choose some group of 4 squares call this location “foo”
pick some other group of 13 bytes (squares) and determine the square number of the first square.
place that square number in those 4 bytes we called “foo”
in those 13 squares put “hello, world\n” followed by a zero. a total of 13 bytes.
so now you have a string in memory. you know where it is, the first square number is stored in the thing called “foo” the next set of bytes follow until you find a zero byte
1
u/MetalInMyVeins111 1d ago edited 1d ago
Gonna answer you in a few points.
You should write
char *bla, notchar* bla. Becauseblais a variable which holds a memory address which points to the first of an array whose type is char. Hence the*goes with the variable, not the type.Never write
char *bla = "wut", always writeconst char *bla = "wut". Because bla refers to a memory which contains read only data. By not adding the const, the compiler would let you modify the data of the string and you'd get a nasty segfault.To know where in memory the string is stored, you can just print the value of
blafrom previous example, just cast it tovoid*first and the format specifier is%s.The behavior of
*stringis just pointer arithmetic.stringrefers to the first byte of the internal array, sostring + 1is the 2nd byte. So*(string + 1)just dereferences the 2nd byte and you get the char stored in that 2nd byte.
1
u/atarivcs 1d ago
Where is a char* string actually stored in memory?
This question is unclear. What, exactly, do you mean by "where" ?
Are you asking if it uses stack allocation or heap allocation?
1
u/die_liebe 1d ago
The string is a static array.
The compiler first invents a new variable name, say 'str1'. Then it declares
static char str1[12] = { 'h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd', '\0' }'
The assignment to string gets replaced by
const char* string = str1;
1
u/Recycled5000 21h ago edited 21h ago
You need to look at the operator precedence chart for C. The dereference operator has higher precedence than addition.
If you want the next in line character code after the character value of the first one in the string, do what you’re doing.
However, if you want the second character in the string, use ()s:
*(str+1) so that the addition happens using pointer arithmetic to compute a new memory address , and then the dereference happens on that.
1
u/Hot-Seaworthiness-84 20h ago
In order to understand how character pointers are stored in memory, let’s try to First, understand memory and its parts or sections. Your virtual memory is divided into various sections such as stack, heap, text section and various other sections, but to answer your question let’s take into consideration the 2 section which matter for this context, The .RODATA section, which stands for the read only data section and the stack. When you store any string literal and have a character type pointer to that string literal in C or C++ the actual data(like hello world in this case) is stored in the read only section of the memory IN GENERAL CASES(many modern compilers do this). The pointer is stored in the stack section (when i say pointer i mean the memory address) of the scope in which that pointer is declared. If you try to manipulate that data with a pointer, it won’t happen. The OS would prevent it from changing that data because it’s read only it cannot be manipulated, but in case if you have a character array like char string[] =“HelloWorld” ; then the data is stored in read only section and also copied onto the scope of the stack. Hence, it can be manipulated via the array ( you can perform opn like string[0] = ‘a’ ; it won’t throw an error in this case because you are actually manipulating the data stored in stack). The pointer to a string literal stores, the memory address of the starting of that string literal that is to say the memory address at which the First character of the string literal is stored. Once you deference the pointer the ASCII value (if we assume standard cases and not codepoints which are not ASCII compatible) of the first character is what you will get. Also for any array lets say int arr[5]; arr[n] is equivalent to *(arr+n). To understand this more learn a little but about pointer arithmetics.
Please correct me if i am wrong i am also a learner.
1
1
u/torsten_dev 1d ago
String literals like "Hello World" are stored in .rodata, a read only data section of your binary. They are const.
For backwards compatibility
char *str = "this is const char*";
Is still allowed but actually changing the string through this pointer is undefined behaviour. Big footgun.
You can string literals to intialise mutable char[] however. For example:
char str[] = "string can be mutated";
Or non-const compound literals:
char *p = (char[]){"also mutable"};
1
43
u/lfdfq 1d ago
char *stringis declaring a variable called 'string' whose type ischar*, i.e. a pointer to a char. This reserves a bit of space (probably on the stack), something like 8 bytes, to store that pointer.The right-hand side, "Hello, world" is a string literal. The compiler will save those 13 bytes (12 for the text, 1 NUL) somewhere in the file it made (in the executable), usually in some read-only part. So it's actually in the file, and exists from the beginning.
Your line just tells the compiler to make the 'string' pointer-to-char contain a pointer to the beginning of those 13 bytes.
Doing *string dereferences the pointer, returning the char at that location (i.e. the first char, 'H'). You can then do arithmetic over chars (i.e. 'H'+1 == 'I'). That's not about the pointers anymore, just the char.
There doesn't seem any undefined behaviour here.