1
#define str(n) n
puts(str(hello));

The above piece of code is working fine in c but the below mentioned code is not giving any output in the console

#define str(n) #n
char* name="David";
puts(strcat(str(hello ),name));

can anyone help me to figure out what is wrong with the second piece of code and please let me know in which cases we can use '#' operator in c and when we shouldn't use that ???

Sai
  • 71
  • 7
  • 1
    You are getting a Segmentation Fault no? – OTheDev Apr 24 '22 at 12:35
  • Why do you want a `str` macro to begin with? – Shane Bishop Apr 24 '22 at 12:36
  • Well yes I am getting segmentation fault when i am debugging the code but it is not showing any output while running. But because of what I am getting segmentation error is there anything wrong in that code ? – Sai Apr 24 '22 at 12:45
  • Where did you learn all of this? – Cheatah Apr 24 '22 at 12:45
  • If you get a segmentation fault, your program stops running. In your case, your program crashes with a segmentation fault before it can output anything. How do you declare the `hello` variable in your code? To use `strcat()`, the first argument to `strcat()` needs to be a `char` array large enough to hold the concatenated string. – Shane Bishop Apr 24 '22 at 12:48
  • Well i know here it doesn't make any sense to use str macro but here I just want a show an example . I used the str macro so I can convert number to a string without a function and can join it with text by strcat function but it is showing me segmentation fault while debugging – Sai Apr 24 '22 at 12:49
  • A segmentation fault is an error that stops program execution, typically because of a memory error. For more info on what a segmentation fault is, see [this question](https://stackoverflow.com/q/2346806/8593689), but be aware that some of the details in the answers might be too advanced for you to fully understand at this stage in your learning. – Shane Bishop Apr 24 '22 at 12:53
  • @Sai, remember always that the preprocessor has its full effect at compile time. Thus, you can use it to convert a token that otherwise with be a numeric *constant* to a string literal, but in that case you can also just write a string literal in the first place. You cannot use stringification to produce a string version of the value of a variable. – John Bollinger Apr 24 '22 at 12:54
  • @John Bollinger can you please elaborate. I can't understand what you want to say – Sai Apr 24 '22 at 12:56
  • @Sai, I am imagining what you might have in mind to use this for, and I'm saying that although `str(1)` will expand to `"1"`, this alternative: `int n = 1; str(n);` will expand to `int n = 1; "n";`. There are occasional use cases for the former, but most of the time, you're better off just writing `"1"` directly. On the other hand, if what you have in mind is along the lines of the latter, then it's just not going to have the effect you seem to want. – John Bollinger Apr 24 '22 at 13:01
  • @John Bollinger yes bro it is giving output n. You mentioned a good point thanks and what do you think is it better to write a function to convert an integer to string – Sai Apr 24 '22 at 13:06
  • 1
    You can use existing functions to convert integers to strings, for example, [`sprintf()`](https://cplusplus.com/reference/cstdio/sprintf/). Or, if you all you doing is outputting to your console, you can just use [`printf()`](https://cplusplus.com/reference/cstdio/printf/?kw=printf). (Note for both `sprintf()` and `printf()` you will need to include `stdio.h`.) – Shane Bishop Apr 24 '22 at 13:11

2 Answers2

3

The issue with the code presented in the question doesn't have anything to do with the stringification operator in particular. Rather, your use of strcat() is invalid.

Remember that stringification produces a string literal, so this ...

#define str(n) #n
char* name="David";
puts(strcat(str(hello ),name));

... is equivalent to this ...

#define str(n) #n
char* name="David";
puts(strcat("hello",name));

strcat attempts to write data into the array specified by the first argument, starting at the position of the string terminator. There are at least three preconditions:

  1. The destination array must be modifiable.
  2. The destination array must contain a string terminator.
  3. There must be enough unused space at the end of the array to accommodate the additional characters.

A string literal is not modifiable and does not contain any extra space, so conditions (1) and (3) are never met when the first argument to strcat() is a string literal. Undefined behavior results from such a call.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Well you say that strcat(str1,str2) function adds the characters of the str2 at end of str1 – Sai Apr 24 '22 at 12:54
  • 1
    No, @Sai, I did not say that. I said that `strcat()` writes data into the array designated by the first argument. That is not the same thing. But out of curiosity, what if I had said what you claim? I don't see where you're going with that. – John Bollinger Apr 24 '22 at 12:57
  • well i am just asking that strcat( ) whether completely changes the first string instead of generating a new string literal or just for time being it changes the first string – Sai Apr 24 '22 at 13:04
  • 1
    @Sai, as far as I can tell, "writes data into the array designated by the first argument" already addresses that question. To be a bit more explicit: no new objects are created. If you're still not tracking then I need some help to understand where you see room for the various behaviors you propose. – John Bollinger Apr 24 '22 at 13:08
  • 1
    @JohnBollinger `str(hello )` is `"hello"` without the space. – OTheDev Apr 25 '22 at 10:25
  • 1
    Yes, ok @oda. Fixed. But this is incidental to the issue. – John Bollinger Apr 25 '22 at 14:36
0

str(hello) or str(hello ) is the string literal "hello". In passing, note that the trailing space character is not included:

If, in the replacement list, a parameter is immediately preceded by a # preprocessing token, both are replaced by a single character string literal preprocessing token that contains the spelling of the preprocessing token sequence for the corresponding argument. Each occurrence of white space between the argument’s preprocessing tokens becomes a single space character in the character string literal. White space before the first preprocessing token and after the last preprocessing token composing the argument is deleted.

(6.10.3.2 The # operator, C99).

In memory this is 'h', followed by 'e', ..., followed by 'o', followed by the null character (a byte with all bits set to 0, typically represented by '\0'):

'h' 'e' 'l' 'l' 'o' '\0'

When you apply strcat like that, it tries to replace '\0' with a 'D'

'h' 'e' 'l' 'l' 'o' 'D'

then make the subsequent bytes 'a', 'v', ..., 'd', '\0':

'h' 'e' 'l' 'l' 'o' 'D' 'a' 'v' 'i' 'd' '\0'

At least two problems with this:

  • bytes after the '\0' have probably not been allocated to you!
  • it is considered undefined behavior if (quote from C99 Standard)

[t]he program attempts to modify a string literal.

For reference, from the C99 Standard, the strcat function has the prototype

#include <string.h>
char *strcat(char * restrict s1, const char * restrict s2);

and description

The strcat function appends a copy of the string pointed to by s2 (including the terminating null character) to the end of the string pointed to by s1. The initial character of s2 overwrites the null character at the end of s1. If copying takes place between objects that overlap, the behavior is undefined.

You should place "Hello" in a char array large enough to also store the name that you want to concatenate if you want your desired output. For example,

#include <stdio.h>
#include <string.h>

#define STRINGIZE(x) #x

int main(void) {

    char *name = "David";
    char a[12] = STRINGIZE(Hello) " ";

    puts(strcat(a, name)); 

    return 0;

}

Output

Hello David

The character array, a, has 12 elements so that it can contain all 5 characters in Hello, the space character, , all 5 characters in David, plus the null character.

Note that 12 is the minimum length for the array a to hold the string "Hello David" (11 characters plus 1 for the null character). You will need a to be larger if the names will be larger. If you know, from your input, the largest possible name, then you can make a large enough to hold the largest possible name (plus Hello plus the null character).

Alternatively, first, you decide on some arbitrary length for the array a. Then, you use the strncat function in a safe way such that strncat only writes to elements of the array a. The strncat function essentially does the same thing as strcat except it allows you to specify a third argument which represents the maximum number of characters from s2 to append to s1 (the null character that strncat automatically appends to the result of the concatenation is not included in this number). In the case that you specify a third argument that instructs strncat to never write past the end of the array a, if the name is too long, the full name will not be stored in a, but this is better and safer than trying to write past the end of the array, which is what strcat would have attempted.

Again, for reference (from C99 Standard), the strncat function has the prototype

#include <string.h>
char *strncat(char * restrict s1, const char * restrict s2,
              size_t n);

and description

The strncat function appends not more than n characters (a null character and characters that follow it are not appended) from the array pointed to by s2 to the end of the string pointed to by s1. The initial character of s2 overwrites the null character at the end of s1. A terminating null character is always appended to the result. If copying takes place between objects that overlap, the behavior is undefined.

OTheDev
  • 2,916
  • 2
  • 4
  • 20
  • 1
    Note that 12 is *just* large enough to fit everything. If `name` was even just one character longer, then the `hello` array would be too small. So in the general case, the `hello` array might need to be given a large allocation to ensure it is big enough for all inputs, and even then it would be safer to use [`strncat()`](https://cplusplus.com/reference/cstring/strncat/?kw=strncat) instead of `strcat()` to avoid a buffer overflow. – Shane Bishop Apr 24 '22 at 13:27
  • Agreed @ShaneBishop. Might be good to add a note about that actually. – OTheDev Apr 24 '22 at 13:28
  • 2
    IMNSHO, suggestions to use `strncat()` are misguided. It has a confusing interface — he says, avoiding NSFW vocabulary. All else apart, if you have enough information to use `strncat()` safely, you could use `memmove()` (or`memcpy()` instead. – Jonathan Leffler Apr 24 '22 at 13:42
  • I am not sure I follow @chux-ReinstateMonica. From the C99 Standard: "The strncat function appends not more than n characters (a null character and characters that follow it are not appended) from the array pointed to by s2 to the end of the string pointed to by s1. The initial character of s2 overwrites the null character at the end of s1. A terminating null character is always appended to the result. If copying takes place between objects that overlap, the behavior is undefined". – OTheDev Apr 24 '22 at 14:47
  • 1
    @oda, My mistake - was thinking about `strncpy()`. – chux - Reinstate Monica Apr 24 '22 at 16:22