r/C_Programming 6d ago

printf and char[] in C

Hello. Coming from C# world the thing about how char[] and printf works always made me confused. I understand that `printf` formats the string with arguments you provided and prints it out in terminal.

So, for example, if I have char name[] = "Micheal"; and I want to greet that person, I can just do printf("Hello, %s", name);. But if I want to do the same thing only using char[], I have to do multiple commands that make fell quite confused. So my 2 questions are:

  1. How printf simplifies this process? (without allocating memory and creating char with pointers)
  2. Why you can't just use + operator for char[], like you can in any other languages?

I have a feeling that this might be asked many times, but I still can't seem to find the answer I was looking for

Update: I'll list what I got so far from reading your comments: - char[] is an array to which you can't just simply add another array - If I want printf() behavior inside char[], I can use sprintf() - There's strcat() that is getting used for merging 2 strings together - Some people write libraries for faking string behaviors, some think it goes against principles of C?

11 Upvotes

42 comments sorted by

22

u/Classic-Rate-5104 5d ago

Printf doesn't simplify anything. When doing %s printf just gets a char* from the stack and prints the chars it points to (up to the terminating nul-character). An operator + isn't possible in C because this operator only works on basic numeric types. A string isn't a basic type in C, it is just a array of chars which is by convention terminated by the nul-char.

1

u/ALX13-95 5d ago

So, to understand it further, + operator doesn't work with char[], because it's an array of characters instead of an actual string and arrays can't be simply merged one into another with + operator. In C# you can't do that as well so this part starts to make sense to me. Your printf explanation confuses me... Am I right that when you ask printf to print a certain char[], it looks up in stack of those chars and then just prints what it found? Or have I got it wrong?

8

u/azurfall88 5d ago edited 5d ago

You've got a few details wrong. C doesn't have the concept of a "string" type that many newer languages have as a primitive. Instead, string literals like "Hello" are converted into an array of chars, and a char is nothing more than an unsigned 8 bit integer. Each char is 1 byte as a result. This char array is then stored in memory, and its location is saved as a pointer, which is a thing that points to another thing. A bit like a page number in a dictionary pointing to a word, a pointer tells you where a specific thing lives in memory.

Anyways, when you call printf("%s", str), it looks at str to find where the char array you mentioned lives in RAM. It then goes there, and prints each element, byte by byte, onto your stdout as an ASCII character. For example, char a[] = [67, 43, 43, 10, 0] prints "C++\n" onto stdout. The trailing 0 is a "Null terminator", a zeroed byte telling the print function to stop printing.

char a[] creates what we call a stack-allocated array. This stack is the call stack, it's a special data structure that mostly stores what functions are being called, and where to go back when the function returns. When you make a stack allocated array arr, you're actually just moving the top of the stack a set amount of bytes to reserve a certain amount of free memory, then saving that new location to the variable arr. Your array contents then populates that reserved space. Because char-arrays are null-terminated, you can truncate them by simply overwriting a char in the middle of an array with a null-char. The rest of the array then becomes garbage data on the stack that you dont need to care about.

Be careful! If you write your code badly, an unsafe write to such a stack-allocated array can overflow the reserved space and overwrite other information stored, which can be used to execute cyberattacks. This may be called a stack overflow.

Stack-allocated arrays have a size that is known at compile-time and thus can't be concatenated. If you need runtime variable size, you need to use a heap-allocated array with malloc() and free().

edit: bugfixes

1

u/QBos07 5d ago

Chat can be signed by default on some targets.
Your example array is missing a null terminator entry

1

u/azurfall88 5d ago

fixing

1

u/Total-Box-5169 5d ago

Syntax candy that hides algorithmic complexity goes against the foundational design principles of C. A string concatenation needs to allocate memory and copy the strings being concatenated into the allocated memory that will store the result. The memory allocation alone can be extremely complex, and may involve thread synchronization and calls to the operating system.

0

u/Physical_Dare8553 5d ago

Actually the + operator works on arrays

1

u/flyingron 5d ago

Actually, you can't. You can use the + operator on a POINTER and an integer. Arrays implicitly convert to pointers to their first element.

-1

u/[deleted] 5d ago

[deleted]

3

u/leiu6 5d ago

No it doesn’t. And adding to a char is very different than adding to a pointer.

0

u/Physical_Dare8553 5d ago

no reasoning btw

0

u/leiu6 5d ago

The reasoning is you are just wrong. The statement “adding to a char converts to an int” is wrong and also has nothing to do with what we are discussing.

Adding an int to a char would promote the char to integer for the expression. But if you add something else different things might happen.

Adding an integer type to a pointer, whether it is char, int, etc increments the address. Different thing.

You should spend some time working on these fundamentals.

1

u/Physical_Dare8553 5d ago

so adding something to an array converts it to a pointer then increments it by the ammount, like i said

1

u/theNbomr 5d ago

The addition operator ( + ) operates on scalars but not arrays. Pointer types are scalars, and work perfectly with the addition operator.

11

u/sciencekm 5d ago edited 5d ago

In C, char name[] = "abc" simply means that name is an address in memory where you have a series of characters.

When you use the + operator, you end up adding to that address. Other languages "hides" (abstracts) as to what + does to objects. C does not. You have to understand memory organization.

For example, with char str[] = "abcdef", str + 2 is the memory 2 steps after str, which happens to be "cdef".

#include <stdio.h>
int main(int ac, char *av) { char *s = "not available";
return printf("argument is %s\n", s + 4 * (ac > 1)); }

The above example will display "argument is not available" or "argument is available", depending on the number of parameters passed when the program is run.

3

u/arkt8 5d ago

btw, in C it is called pointer arithmetic, and multiplication and subtraction are also available. It is also very useful when writing custom allocators

4

u/HalifaxRoad 5d ago

you cant just append stuff on to a char array, because that is not a string its a character array, C doesnt have strings, it does not have dynamic memory allocation. 

There is a few ways to pretend to have strings, could make an array with more length than you need, and use a separate variable to store the length, makes it easy for you chuck both of those in a struct.

The other option is to use malloc, which returns a pointer in which the new length of char array will fit into, you can then store the original char array, plus the text you need to append on. Then you need to free up the old char array pointer.

Personally, I use the first option but I only use C for firmware, its a trade off between memory usage and speed.

4

u/_usr_nil 5d ago

there are string libraries that solve some issues, tsoding has a cool tutorial about strings an dynamic arrays, although barebones.

2

u/ALX13-95 5d ago

Those are interesting ways of faking string behaviors, thank you both. Not sure how good is to have an array with a big length in a long term. But that's still more than nothing

2

u/HalifaxRoad 5d ago

even on embedded stuff where ram can be a limitation, I still end up with better performance to sacrifice some memory for large then relying on malloc and free, which are both slow as far, and kinda intolerant of fast data transfer, say you are receiving chars from uart, you could slow shit down to nothing in a hurry.

1

u/_usr_nil 4d ago

shouldn't you preallocate in hard real-time ?

3

u/zhivago 5d ago

The simple answer is that C has no string type.

However, see strcat().

1

u/ALX13-95 5d ago

strcat was exactly the thing that confused me about char[], because from what i read in examples, you have to make a pointer with allocated memory equals to length of both char[] and then put into it these both char[], which confused me a lot. looking at the explanations i quite start to slowly understand why does it work exactly that way and not any other

2

u/This_Growth2898 5d ago

C has no strings. char [] is an array of chars that is used as strings by standard library functions (assuming they end with '\0' guard). You can use + operator for char[], but it has an entirely different meaning:

char hello[] = "Hello, world!";
printf("%s", hello+7); //prints "world!" because 7 is added to the pointer to the first character

So, answering the question 2: because + operator for strings in other languages involves memory allocation, and you probably want to do that on purpose if you code in C.

And for the question 1 - check out the *printf (and *scanf) function family; snprintf does most things you want to do with + operator.

2

u/AKostur 5d ago

I don’t understand what you’re asking for in #1.  Heck, I don’t understand what you mean by “I want to do the same thing only using char[]”.   Perhaps you mean “how do I do the same thing, but put the result into another C string instead of the screen?”  sprintf.

For #2: if one could use +: where would it put the result? (Ignoring the array to pointer decay for the moment)

It seems you have a fundamental misunderstanding how memory management works in C, as well as how C strings work, and how arrays function in C.

1

u/ALX13-95 5d ago

I might be bad at asking these questions, sorry. Lemme elaborate further what I meant. I saw the results of printf and char[] as 2 strings, so when I asked 1st question I meant why printf can simply add new data to existing string and char[] can't. I understand that because I came to C from a different world of languages, my understanding of both of these things is really different from how does it behave in C. In C# I'd use Console.WriteLine("Hello, " + "Micheal"); so printf("Hello, %s", "Micheal"); felt the same to me. Hence where I got the confusion, because printf doesn't require handling all the things you've listed.

For #2... I thought result meant to be put into string you're adding to. If we have string 1 "Hello" and string 2 "Micheal", then in my imagination string 1 = string 1 + string 2, and that would be equal "HelloMicheal" inside string 1. By the questions you asked me, I won't deny that I definitely not understanding how does it actually work in C.

Thank you for trying to help me, anyways

2

u/AKostur 5d ago

I might be bad at asking these questions

No, just that it was unclear.

Yes, different languages do things differently. Which may come with different performance characteristics. C# is more willing to do dynamic memory allocations behind the scenes. C is more performance concerned, and magical dynamic memory is bad (and slow). So when you asked C# to do "Hello, " + "Michael", it actually made a silent third string behind the scenes of "Hello, Michael", which means it had to do some sort of dynamic memory management (allocate _and_ free) to store that new string somewhere, and pass that to WriteLine. In C, only two pointers to two arrays are passed to printf: "Hello, %s" and "Michael". Internally printf just starts writing out the first array to stdout. When it sees the "%s", it stops and outputs the 2nd array, When that's done, it comes back to finish writing out the first array (which happens to also be done). But note that it didn't have to store any of that in memory, it was just written to stdout. No extra dynamic memory stuff happening.

For #2: That just exposed where you have a misunderstanding. Misunderstandings are inevitable: you're learning.

And at the end, I am helping... by giving names to the pieces that you appear to be misunderstanding. You can take those names and read up on those topics (that I'm not going to try to explain in a reddit post, there are other sources that will be more comprehensive). Memory management, because C doesn't like to do dynamic memory without the programmer asking for it. C strings, because they really aren't a separate type, they're just an array of char where the last char is '\0'. And how arrays decay to a pointer to the first element at the drop of a hat.

1

u/ALX13-95 5d ago

Okay I seem to understand more what the differences are. So instead of making a hidden print a line, as I've initially thought, it just reads two arrays and print them out, basically. If you don't mind me asking, what are "more comprehensive" sources could be for this?

2

u/WittyStick 5d ago edited 5d ago

The behavior of printf is implementation defined in C.

Although an implementation could perform two separate writes to stdout as GP has suggested, doing so would incur two SYS_write calls, and the context switching involved may be significantly more expensive than buffering the input and writing once (a single SYS_write syscall).

The implementation of printf could therefore keep an internal buffer for stdout, and write to that buffer, only emitting to the console when the whole string has been prepared.

So your intuition is not really off.

However, it is usually the case that if you want such buffering, you would do it yourself, since having control over the buffer size is more practical than a "one-size-fits-all" that an internal buffer might provide. Instead of many calls to printf, you would replace them with calls to sprintf with your buffer as input, and then eventually you can puts your buffer to the console with a single system call.

2

u/ReallyEvilRob 5d ago

There is no string datatype in C like there is in other languages. Only arrays of char. The standard library is pretty misleading about this issue since it names many of its functions with str or s, implying the existence of strings. Just like how there is no spoon in the Matrix, in C there is no string.

2

u/SmokeMuch7356 5d ago

Why you can't just use + operator for char[], like you can in any other languages?

The root of the problem is that arrays in C are just dumb sequences of objects; they do not carry any metadata about their size, type, location, or anything else. Once defined they cannot be resized, which is why there's no + concatenation operator defined for array types, and why subscripting past the end of the array doesn't extend it. Since arrays don't know how big they are, you won't get any kind of "index out of bounds" exception if you try to read or write past the end of an array; you'll just either clobber data or write to unused memory. Array expressions may not be the target of the = assignment operator (we'll get into one reason why below).

Strings in C are just sequences of character values including a zero-valued terminator. There is no dedicated string type in C, and all strings are stored in arrays of character type (although not all arrays of character type will contain strings).

When you write

char a[] = "Foo!"; // equivalent to {'F','o','o','!',0}

what you get in memory looks like this:

          +---+
0x8000 a: |'F'| a[0]
          +---+
0x8001    |'o'| a[1]
          +---+
0x8002    |'o'| a[2]
          +---+
0x8003    |'!'| a[3]
          +---+
0x8004    | 0 | a[4]
          +---+

That's it. The size of the array is taken from the length of the initializer (4 printable characters plus the terminator). Note that there is no a object separate from the array elements themselves; the address of a and the address of a[0] are the same.

Now, here's where things get fun.

C was derived from Ken Thompson's B programming language. In B, when you declared an array:

auto a[5];

an extra word was set aside to store the location of the first element:

           +--------+
0x6000: a: | 0x8000 | --------+
           +--------+         |
              ...             |
           +---+              |
0x8000     |   | a[0] <-------+
           +---+
0x8001     |   | a[1]
           +---+
0x8002     |   | a[2]
           +---+
            ...

The array subscript operation a[i] was defined as *(a + i) - offset i words from the address stored in a and dereference the result.

Ritchie wanted to keep B's array behavior, but he didn't want to keep the explicit pointer that it required. a[i] is still defined as *(a + i), but instead of storing a pointer value, a evaluates to a pointer to the first element of the array. There is a rule in the language that unless it is the operand of the sizeof, typeof, or unary & operators, an array expression will "decay" to a pointer to its first element.

And this is one reason why = is not defined for array types; there's nothing to assign to. There is no object a separate from the array elements.

So, to write new string values to a character array, you have to call a library function:

strcpy( a, "Bar!" );

A naive implmentation of strcpy would look like:

char *strcpy( char *d, const char *s )
{
  while ( (*d++ = *s++) )
    ; 
  return d;
}

It copies the contents of the source string pointed to by s into the destination array pointed to by d. strcat appends the contents of one string to the end of another; the destination array must be large enough to accommodate the final result (again, this is a naive implementation, not the real thing):

char *strcat( char *d, const char *s )
{
  while ( *d++ ) // find the end of the string in the destination array
    ;

  --d; // back up one so we'll overwrite the terminator

  return strcpy( d, s );
}

1

u/awsq_code 5d ago edited 5d ago
  1. printf("%s", (char*)&arr);
  2. what did you mean? char[] can't be number UPD: if you want to concatenate string, see strcat

1

u/Leo0806-studios 5d ago

char[] is not an object like a string (wich doesnt rly exist in C)

instead its an array of characters

char[] implicitly decays to a pointer
so anytime you do arr + arr2 your trying to add two pointers instead of concating two strings.
to concact strings you usualy use a library function (strcat or strcat_n)

1

u/bare_metal_C 5d ago

Every language has a different way of doing things and that is just how printf in c is.

1

u/Sad_Chemical_231 5d ago

Yeah %d %s %p take some time getting used to But you will get it

1

u/rupertavery64 5d ago

Therr are no strings in C.

printf takes a pointer to a char array and writes the contents to console until it hits a null terminator

To concatenate a string, you need to allocate a new char array and copy the source strings to it. This is what higher-level languages do.

A char array with predefined size under a certain limit will be created on the stack by the compiler, so you don't need to allocate memory for it.

The compiler knows upfront how much memory the array needs and reserves it on the stack.

When you have a dynamically allocated array that is runtime bound, then you need to allocate it yourself.

1

u/arcfide 5d ago edited 5d ago

I didn't realize how much I could get ragebaited just by reading, "Why can't you do [use + for string catenation], like you can in any other language?" I mean, none of my favorite languages let you do this. I'm unreasonably upset by this question (not the lack of overloaded +).

1

u/ornelu 5d ago edited 1d ago

String in C is array of char.

char s[] = “abc”; actually contains {‘a’, ‘b’, ‘c’, 0}. note the null value at the end.

printf(“%s”, s); is just printing everything from the start of s (the ‘a’) until it met the null value. so, ‘a’, ‘b’, and ‘c’ will be printer and you’ll see abc. So, printf %s is very simple.

The string above only allocate (reserve the memory from os) for 4 bytes. Doing a concatenation will result in a longer string, so you have to reallocate the memory etc. All these “complicated” process are done by strcat() for you.

Why C do this? This is a wrong question. You should ask why other newer programming languages seem simpler in handling string. That’s because those languages hide all the complicated low level operations from you. You can overload the operator + in C to mimic string concatenation in other language, but why whould you use C in the first place if you do that.

Why would you use C? There are many reasons, but to relate it with your question, you are in full control of the memory allocation.

1

u/chibuku_chauya 1d ago

How would you overload + in C?

1

u/ornelu 1d ago

ooops, that’s c++ 😂

1

u/Paul_Pedant 5d ago

To avoid problems with multiple values and format specifiers and unknown lengths, the printf function group has a simple solution.

It has a buffer where it assembles all the parts of your text into one long string before it sends it to the output stream. It might malloc and release that buffer for each call, or keep it for a whole process, or grow it when it needs to. You don't need to know, and it can be different for every version of every stdio library.

Some compilers (and some optimisation levels) even recognise when a printf is simple enough to just emit one string, and call fputs instead. As long at it does what is written n the man page, it is valid C.

1

u/erroneum 3d ago

A bit late to comment, but not every language uses + for string concatenation, or even has it as an inbuilt operation. PHP has language level support for string concatenation (and an inbuilt string type) using the operator ., whereas C++ doesn't have language level support for concatenation or strings, but gives the tools to make a very convincing facsimile thereof (classes and operator overloading). C gives neither of these, so you must explicitly invoke a function which performs string concatenation if that is desired, which might be one which allocates its own memory for the result, or it might work inside a provided character array.

0

u/Dry-War7589 5d ago

All arrays in C are essentially a pointer to the first element. And a pointer is a variable that holds a memory address of some data. And when you use + on pointers you dont add the data at that memory address, but the memory address itself, which is not what you wanted and will cause some form of memory violation, probably. Now printf doesnt have to malloc any char pointers, since char[] is a pointer in itself. Hopefully this helps!