Home > Net >  problem while using stringize operator in c
problem while using stringize operator in c

Time:04-25

#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 ???

CodePudding user response:

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.

CodePudding user response:

str(hello) or str(hello ) is the string literal "hello".

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'

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.

  • Related