Problem on strncpy with source string longer than destination array - c

I tried to think how to make the function strncpy and I met this problem.
char src[] = "123456789A";
char dest[10];
int n = 10;
printf("strncpy:%s\n", strncpy(dest, src, n));
Output
strncpy:123456789A123456789A
What is happening ?

The quick answer: strncpy is not your friend!
strncpy is not a safer version of strcpy, it will copy up to n characters from src and if src is shorter, will pad the destination with null bytes up a total of n characters.
If the source string has n or more characters, the destination array will not be null terminated and passing to printf("%s", will have undefined behavior: printf will keep reading and printing bytes from memory after the end of dest, until it finds a null byte or until this undefined behavior causes other unpredictable side effects...
The semantics of strncpy are counter-intuitive and error-prone, avoid usng this function. See this article for a long answer: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

As others have said strncpy won't include a terminating null if the destination size is the same as the string length. To give you a practical answer I normally just subtract one from the size of the destination using sizeof to get the destination size including space for the terminator:
char src[] = "123456789A";
char dest[10];
printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));
Which gives an output of "strncpy:123456789" which is a character short of what you want but at least is defined behaviour and lets you know the destination buffer isn't large enough to contain the null terminator. So the final code that gives you the result you're after would be:
char src[] = "123456789A";
char dest[11];
printf("strncpy:%s\n", strncpy(dest, src, sizeof(dest) - 1));

The array dest does not contain a string because there is no enough space to accommodate the terminating zero '\0' of the copied source string,
So to output the array use the following statement
printf("strncpy: %*.*s\n", n, n, strncpy(dest, src, n));
Otherwise you have to write something like the following
strncpy( dest, src, n )[sizeof( dest ) - 1] = '\0';
printf("strncpy: %s\n", dest );
In this case the destination array will not have the last character of the source string that will be overwritten by the zero character.
If you want to copy less characters than the size of the destination array then what to do after copying depends on the intention. If you want just to overwrite part of the string that is already stored in the destination array then nothing else you need to do. Otherwise set the character at position n to zero character.
Here is a demonstrative program.
#include <stdio.h>
#include <string.h>
int main(void)
{
char src[] = "123456789A";
char dest[10] = "543216789";
size_t n = 5;
strncpy( dest, src, n );
printf("strncpy: %s\n", dest );
strncpy( dest, "Hello", n )[n] = '\0';
printf("strncpy: %s\n", dest );
return 0;
}
Its output is
strncpy: 123456789
strncpy: Hello

Related

creating a string from 2 substrings

I want to create a big string 'des' from 2 substrings copied from string 'scr' this way :
I want to copy the substring, lets call it - 'string1' ( from ptr x to the end of the 'scr' string), then to place it in the 'des' and after that to concatenate it with another substring, lets call it - 'string 2' copied from the 'scr' string from the head of the string to x ptr. How can I do it not using a temp string ?
for example : scr = "ThisIs", string1 = "Is", string2 = "This" des = "IsThis"
I don't want to use a temp string to hold string 1 or string2.
Can you help me?
You don't need a temp string, you only need a pointer to hold the boundary of substrings. Try following:
char src[] = "ThisIs";
char dst[7] = {'\0'};
int len = strlen(src);
int str1len = 4;
strncpy(dst, src + str1len, len - str1len);
strncpy(dst + len - str1len, src, str1len);
printf("src=%s, dst=%s\n", src, dst);
If you know the position of the second string, you can just print the two substrings to the destination string in reverse order:
char *src = "ThisIs"; // source string
char dst[7]; // char buffer for destination string
int pos = 4; // position of second substring
snprintf(dst, sizeof(dst), "%s%.*s", src + pos, pos, src);
puts(dst);
Explanation:
snprintf writes formatted data to a string, just as printf writes formatted data to the screen. It takes the buffer length as second argument and uses it to ensure that the buffer will not overflow. It also guarantees that the resulting string is null terminated.
If the output would be a string with more characters than the buffer can hold, the output is truncated. snprintf returns the length that the string would have if the buffer were ininitely large. You can use that return value to check whether the output was truncated.
The second substring is null-terminated, because it ends where the whole string src ends. You can print it with printf("%s", str + pos), where pos is the start of the substring.
The first substring isn't null terminated. You can print substrings of any length by providing a "precision" to the %s format after a period: printf("%.4s", str).
You can make that precision variable by using an asterisk in the format and then providing an additional int argument before the actual argument: printf("%.*s", 4, str)
This answer is at heart the same answer as fluter's, but it guards against buffer overfloows and involves fewer length calculations.

C program printing weird characters

I have a program that reads the content of a file and saves it into buf. After reading the content it is supposed to copy two by two chars to an array. This code works fine if I'm not trying to read from a file but if I try to read it from a file the printf from buffer prints the two chars that I want but adds weird characters. I've confirmed and it's saving correctly into buf, no weird characters there. I can't figure out what's wrong... Here's the code:
char *buffer = (char*)malloc(2*sizeof(char));
char *dst = buffer;
char *src = buf;
char *end = buf + strlen(buf);
char *baby = '\0';
while (src<= end)
{
strncpy(dst, src, 2);
src+= 2;
printf("%s\n", buffer);
}
(char*)malloc(2*sizeof(char)); change to malloc(3*sizeof*buffer); You need an additional byte to store the terminating null character which is used to indicate the end-of-string. Aslo, do not cast the return value of malloc(). Thanks to unwind
In your case, with strncpy(), you have supplied n as 2, which is not having any scope to store the terminating null byte. without the trminating null, printf() won't be knowing where to stop. Now, with 3 bytes of memory, you can use strcpy() to copy the string properly
strncpy() will not add the terminating null itself, in case the n is equal to the size of supplied buffer, thus becoming very very unreliable (unlike strcpy()). You need to take care of it programmatically.
check the man page for strncpy() and strcpy() here.

Size definition of strcat() function

The question is why should I define size of string (string[] should be string[some-number])
When the program is as following it gives me Abort trap: 6:
#include <stdio.h>
#include <string.h>
int main(void)
{
char buffer1[] = "computer";
char string[]="program";
strcat( buffer1, string );
printf( "buffer1 = %s\n", buffer1 );
}
This is the program from http://www.tutorialspoint.com/cprogramming/c_data_types.htm it works fine:
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[12] = "Hello";
char str2[12] = "World";
char str3[12];
int len ;
/* copy str1 into str3 */
strcpy(str3, str1);
printf("strcpy( str3, str1) : %s\n", str3 );
/* concatenates str1 and str2 */
strcat( str1, str2);
printf("strcat( str1, str2): %s\n", str1 );
/* total lenghth of str1 after concatenation */
len = strlen(str1);
printf("strlen(str1) : %d\n", len );
return 0;
}
What is the mistake? Even if I define all of the sizes of strings in my program, my code still gives Abort trap:6?
From the man page of strcat:
DESCRIPTION
The strcat() function appends the src string to the dest string, overwriting the termiā€
nating null byte ('\0') at the end of dest, and then adds a terminating null byte. The
strings may not overlap, and the dest string must have enough space for the result. If
dest is not large enough, program behavior is unpredictable; buffer overruns are a
favorite avenue for attacking secure programs.
When you declare your string, the compiler allocate the size of your initial string to be 9 (resp. 8) for the buffer1 (resp. string) (includin '\0').
Thus, strcat will result in 9 - 1 + 8 (i.e. 16 bytes) but only 9 are available.
Your strcat is buffer overflowing buffer1 which can hold only strlen("computer")+1 bytes. ommitting array size does not mean "dynamic" array! When you specify the size of the array, you are reserving as many bytes as you want: again you need to avoid bufferoverflow of course.
So,
strcpy(str3, str1);
and
strcat( str1, str2);
are ok since str3 size is enough for str1, and str1 is enough for strlen(str1) + strlen(str2) + 1, i.e. exactly 11: 5 (hello) + 5 (world) + 1 (terminator). The magic number 12 was choosen with a reason, big enough to hold both strings and a terminator.
About C strings
C-strings are array of chars where the last is "null", '\0', i.e. they are array of chars where the last one is 0. This terminator is needed so that string related functions can understand where the string ends.
If it happens that a null byte is found in the middle of a string, from the point of view of C string functions, the string will end at that point. E.g.
char buffer1[] = "computer\0program";
// array: { 'c', 'o', ... '\0', 'p', 'r', 'o', .., 'm', '\0' }
// ...
printf("%s\n", buffer1);
will print computer only. But at this point the buffer will be big enough to hold computer and program, a terminator (and another extra byte), since the compiler computed the size of the char array considering the literal sequence of characters which syntactically ends at the second ".
But for all C-string functions, the string contained in buffer1 is computer. Note also that sizeof buffer1 will give the correct size of the buffer, i.e. 17, opposed to the result of strlen(buffer1) which is just 8.
The first parameter of strcat is used to store the result, so it must have enough space for the concatenated string.
In your code:
char buffer1[] = "computer";
is equivalent to:
char buffer1[9] = "computer";
defines a char array with just enough space for the string "computer", but not enough space for the result.
char buffer1[] = "computer";
Creates a buffer big enough to hold 9 characters (strlen("Hello" + 1 byte for \0)). If you write anymore data to it what you end up with is Undefined behavior (UB). This is what happens when you do a strcat.
UB means the program might crash or show literally any behavior. You are rather lucky that a program with UB crashes because it does not need to, but if it does atleast there is a indication of something wrong in it. Most of the times programs with UB will continue running correctly and crash when you least expect or want them to.

c string copy with strncpy

I am really frustrated about strncpy function. I did something like this:
char *md5S; //which has been assign with values, its length is 44
char final[32];
strncpy(final,md5S,32);
but somehow the length of char final[] became more than 32 after.
What should I do here?
You forgot to leave room for the null character
char final[33];
strncpy(final,md5S,32);
final[32] = '\0';
strncpy does not NUL-terminate the copied string if the source string is as long as or longer than n characters. If you have access to it, you can use strlcpy, which will NUL terminate for you.
Do this instead:
strncpy(dst, src, dstlen - 1);
dst[dstlen - 1] = '\0';
OR
strlcpy(dst, src, dstlen);
where, for a char array, dstlen = sizeof(dst).
If your output buffer isn't large enough to copy all of the source string and its trailing NUL then the output string will not be NUL terminated.
In your case, the MD5 is 33 bytes including the NUL, so the NUL isn't copied. When you read the string back you're reading past the end of your buffer.
Make final 33 bytes long, and ALWAYS add a NUL to the last character in the destination when using strncpy.
int n = 32;
char *src = "some really really long, more than 32 chars, string."
char dst[n+1];
strncpy(dst, src, n);
dst[n] = '\0';

How to truncate C char*?

As simple as that. I'm on C++ btw. I've read the cplusplus.com's cstdlib library functions, but I can't find a simple function for this.
I know the length of the char, I only need to erase last three characters from it. I can use C++ string, but this is for handling files, which uses char*, and I don't want to do conversions from string to C char.
If you don't need to copy the string somewhere else and can change it
/* make sure strlen(name) >= 3 */
namelen = strlen(name); /* possibly you've saved the length previously */
name[namelen - 3] = 0;
If you need to copy it (because it's a string literal or you want to keep the original around)
/* make sure strlen(name) >= 3 */
namelen = strlen(name); /* possibly you've saved the length previously */
strncpy(copy, name, namelen - 3);
/* add a final null terminator */
copy[namelen - 3] = 0;
I think some of your post was lost in translation.
To truncate a string in C, you can simply insert a terminating null character in the desired position. All of the standard functions will then treat the string as having the new length.
#include <stdio.h>
#include <string.h>
int main(void)
{
char string[] = "one one two three five eight thirteen twenty-one";
printf("%s\n", string);
string[strlen(string) - 3] = '\0';
printf("%s\n", string);
return 0;
}
If you know the length of the string you can use pointer arithmetic to get a string with the last three characters:
const char* mystring = "abc123";
const int len = 6;
const char* substring = mystring + len - 3;
Please note that substring points to the same memory as mystring and is only valid as long as mystring is valid and left unchanged. The reason that this works is that a c string doesn't have any special markers at the beginning, only the NULL termination at the end.
I interpreted your question as wanting the last three characters, getting rid of the start, as opposed to how David Heffernan read it, one of us is obviously wrong.
bool TakeOutLastThreeChars(char* src, int len) {
if (len < 3) return false;
memset(src + len - 3, 0, 3);
return true;
}
I assume mutating the string memory is safe since you did say erase the last three characters. I'm just overwriting the last three characters with "NULL" or 0.
It might help to understand how C char* "strings" work:
You start reading them from the char that the char* points to until you hit a \0 char (or simply 0).
So if I have
char* str = "theFile.nam";
then str+3 represents the string File.nam.
But you want to remove the last three characters, so you want something like:
char str2[9];
strncpy (str2,str,8); // now str2 contains "theFile.#" where # is some character you don't know about
str2[8]='\0'; // now str2 contains "theFile.\0" and is a proper char* string.

Resources