Why does this implementation of 'strncpy' work? - c

I have to rewrite for an assignment a function that mimics the behavior of the strncpy, after many trials and error, some external help, here's the final code :
15 char *ft_strncpy(char *dest, char *src, unsigned int n)
16 {
17 unsigned int i;
18 unsigned int size;
19
20 i = 0;
21 size = 0;
22 while (src[i] && i < n)
23 {
24 dest[i] = src[i];
25 i++;
26 }
27 while (i < n)
28 {
29 dest[i] = '\0';
30 i++;
31 }
32 return (dest);
33 }
It works flawlessly, but I don't get this part :
while (i < n)
{
dest[i] = '\0';
i++;
}
At this point, the value of i should be (n - 1) right ? So '\0' goes into dest[n-1] and the loop ends because i becomes equal to n and then the function ends.
We're left with a string that would look something like this :
"string copied\0not copied part"
And be printed like : string copiednot copied part.
My question is :
Why does dest[n-1]='\0' or dest[n]='\0' instead of that while loop, return string copied instead of 'string copiednot copied part' when they basically do the same thing ?
Why does the \0 seem to be 'ignored' when printed out after the while loop, when it's considered a full stop when I use dest[n-1] = '\0' instead ?
Here's the main/alternative function I used to run test and try to understand :
int main()
{
char str[] = "test de chaine";
char *str2 = "chaine de test";
ft_strncpy(str, str2, 6);
printf("%s", str);
return 0;
}
char *ft_strncpy(char *dest, char *src, unsigned int n)
{
unsigned int i;
unsigned int size;
i = 0;
size = 0;
while (src[i] && i < n)
{
dest[i] = src[i];
i++;
}
dest[n-1] = '\0';
return (dest);
}

the value of i should be (n - 1) right ?
This is not necessarily the case, since the first while loop exits when it encounters a \0 character (even if i is less than n-1).
The while-loop is used to ensure the remainder of the dest array is properly initialized to \0.
The "wrong" behavior you are seeing (the string being printed beyond the end of the copied string) is due to the fact that both loops exit with the same condition: the first loop exits when i becomes n and since the second loop has the check i<n, it does not run.
This corresponds to the behavior described in the manual:
The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.
If you were to copy a string str with the value abc, it would properly display it in the example below:
#include <stdio.h>
char *ft_strncpy(char *dest, char *src, unsigned int n)
{
unsigned int i;
unsigned int size;
i = 0;
size = 0;
while (src[i] && i < n)
{
dest[i] = src[i];
i++;
}
while (i < n)
{
dest[i] = '\0';
i++;
}
return (dest);
}
int main()
{
char str[] = "test de chaine";
char *str2 = "abc";
ft_strncpy(str, str2, 6);
printf("%s\n", str);
return 0;
}

strncpy() is specified as writing exactly n bytes, independently of the length of the source string.
strncpy(dst, "foo", 5); // writes 'f', 'o', 'o', 0, and 0
strncpy(dst, "foo", 3); // writes 'f', 'o', and 'o': dst is not a string now (*)
strncpy(dst, "foo", 1); // write 'f': dst is not a string now (*)
// (*) unless you make sure there is a '\0' somewhere else in dst

You need to understand that while they look similar,a char array is not a string while a string is a char array.Some functions (i.e. string.h ones) will read each byte one after the other and will stop when they meet '\0'.So check this example and you'll have a different view.
int main()
{
char str3[] = "This is a test";
printf("\n %s", str3);
str3[5] = '\0';
printf("\n %s \n", str3);
int i = 0;
for(i = 0 ; i < strlen(str3) ; i ++)
{
printf(" %c -",str3[i] );
}
printf("\n");
for(i = 0 ; i < 12 ; i ++)
{
printf(" %c -",str3[i] );
}
return 0;
}
What we print is not what we have in memory.Especially using %s will stream the char array and stop when hit '\0'.
Is what happens in this example clear to you?

Related

C program copy string without using strcpy() with enough memory

I am trying to have this output:
Comparing results of concat and strcat ...
strcmp("Plain old stringTroy", "Plain old stringTroy") says: 0
The strcmp returns 0 if the two string arguments are identical. If the result is 0, then the concat behaves exactly like the library function strcat.
this is what I have for concat method.
#define MAXSIZE 32
void concat(char dest[], char src[])
{
int i=length(src);
int j=0;
for(j; j<src[j] !='\0'; j++) {
dest[i+j] = src[j];
}
dest[i+j] = '\0';
}
length method is:
int length(char str[])
{
// Add code here to return the length of the
// string str without using the strlen function
// Do not count the null character '\0'
// in computing the length of the string
int len=0;
int i;
for(i=0;i<str[i];i++) {
len++;
}
return len;
}
This is my main
int main()
{
// Variable declarations for all parts
char str2[] = "Troy";
char str4[] = "Plain old string";
char str6[MAXSIZE];
// Part 6
printf("\n----- Part 6 -----\n");
// Make a copy of the destination string first, to be reused later
strcpy(str6, str4);
concat(str4, str2);
strcat(str6, str2);
printf("Comparing results of concat and strcat ...\n");
printf("strcmp(\"%s\", \"%s\") says: %d\n",
str4, str6, strcmp(str4, str6)
);
return 0;
}
This is my output when I run it:
----- Part 6 -----
Comparing results of concat and strcat ...
strcmp("PlaiTroy", "Plain old stringTroy") says: -1
The first string is not the same as the second string which is why I am getting a -1. My problem is in my concat method but I can't seem to understand why it won't execute well. Is it because of the spaces? Is 0 and '\0' not executing well?
There are multiple problems in your code:
The loop test in the length function is incorrect: instead of i < str[i], it should be:
for (i = 0; str[i] != '\0'; i++)
the same problem in the concat function. Change the loop to:
for (j = 0; src[j] != '\0'; j++) {
also in the concat function, i should be the length of dst, not that of src. You might use len instead of i for this variable.
The array str4 in function main does not have any space available at the end for concat to append anything. Define it with a larger size this way:
char str4[MAXSIZE] = "Plain old string";
Here is the corrected version:
#include <stdio.h>
#include <string.h>
#define MAXSIZE 32
void concat(char dest[], char src[]) {
int len = length(dest);
int j;
for (j = 0; src[j] != '\0'; j++) {
dest[len + j] = src[j];
}
dest[len + j] = '\0';
}
int length(char str[]) {
int len = 0;
int i;
for (i = 0; i < str[i]; i++) {
len++;
}
return len;
}
int main(void) {
// Variable declarations for all parts
char str2[MAXSIZE] = "Troy";
char str4[MAXSIZE] = "Plain old string";
char str6[MAXSIZE];
// Part 6
printf("\n----- Part 6 -----\n");
// Make a copy of the destination string first, to be reused later
strcpy(str6, str4);
concat(str4, str2);
strcat(str6, str2);
printf("Comparing results of concat and strcat ...\n");
printf("strcmp(\"%s\", \"%s\") says: %d\n",
str4, str6, strcmp(str4, str6));
return 0;
}
You have several problems in both functions:
concat
for(j; j<src[j] !='\0'; j++) {
What is the for exit condition here?, src[j] != '\0' is enough.
dest[i+j] = src[j];
Here you add data with an offset of i, but I is the length of src, not dst.
So the corrected function could be:
void concat(char dest[], char src[])
{
/* descriptive variable name */
int len_dst = length(dst);
int j=0;
/* clear exit condition */
for(; src[j] != '\0'; j++) {
dest[len_dst+j] = src[j];
}
dest[len_dst+j] = '\0';
}
length
for(i=0;i<str[i];i++) {
Same remark, what is this exit condition? src[i] != '\0' is enough
So the corrected function could be:
int length(char str[])
{
int len=0;
int i;
/* clear exit condition */
for ( i=0; str[i] != '\0'; i++) {
len++;
}
return len;
}
main
And warning in main function:
char str4[] = "Plain old string";
concat(str4, str2); /* <- erases what is after str4 */
You do not have enough space to store result. Write something like:
char str2[] = "Troy";
char str4[MAXSIZE] = "Plain old string"; /* <-- reserve spaces after str4*/
/* ... */
concat(str4, str2);

Trying to terminate a char array and print afterwards

I'm making a char array binary representations of a number. I want to terminate the end of the string with '\0'. It prints the array find aside from the intended garbage value.
However when I try to terminate the array, specifically the index of the garbage value, it won't print out my string.
int main(void)
{
char s[8] = {0};
printing_binary(s, 8, 2);
}
void printing_binary(char *s, int length, unsigned n){
for(int i = 0; i < length; i++){
if(1 & (n >> i)){
s[i] = '1';
}
else{
s[i] = '0';
}
}
//Trying to terminate my string
//s[length] = '\0';
printf(s);
}
This code will print out something like 01000000}.
However if I uncomment out s[length] = '\0', it prints nothing/the program stops executing. I've tried printing out "Hello world" after everything.
If you define your result array as char s[8], then you must not write anything at position 8 like in s[length] = 0x0 with length==8, because this exceeds the array length. Try:
char s[9] = {0};
printing_binary(s, (9-1), 2);
You have an 8 bit length which you fill up with the values. For example the value 2 is 00000010 Since you have filled up your string array, you have no room for the ending character. If you define the array size as length+1, then it will work.
int main(void)
{
char s[9] = {0}; // Note size is length+1
printing_binary(s, 8, 2);
}
void printing_binary(char *s, int length, unsigned n){
for(int i = 0; i < length; i++){
if(1 & (n >> i)){
s[i] = '1';
}
else{
s[i] = '0';
}
}
//Trying to terminate my string
s[length] = '\0'; // s must be an array of size greater than length
printf(s);
}
If you don't want to add a null terminator, you just have t modify the printf slightly.
int main(void)
{
char s[8] = {0};
printing_binary(s, 8, 2);
}
void printing_binary(char *s, int length, unsigned n){
for(int i = 0; i < length; i++){
if(1 & (n >> i)){
s[i] = '1';
}
else{
s[i] = '0';
}
}
//Trying to terminate my string
//s[length] = '\0';
printf("%.*s", length, s);
}
Another way is to declare i outside the loop. This can be useful if for some reason your logic breaks out of the loop before reaching length
int i;
for ( i=0; i < length; i++ ) {
...
}
s[i] = '\0';

Writing to char array, empty output

I'm wondering what's occurring here, I allocate space for the dest variable outside of the reverse function. And then write to it in the function call. But my output is blank. I want to do this without malloc, purely for educational purposes. Thanks
#include <stdio.h>
#include <string.h>
/*function declaration*/
void reverse(char *src, char *dest);
int main(void) {
char src[] = "hello, world";
char dest[strlen(src)];
reverse(src, dest);
printf("%s\n", dest);
return 0;
}
/*function reverse a string*/
void reverse(char *src, char *dest) {
int i;
int j = 0;
for(i = strlen(src); i >= 0; i--) {
*(dest+j) = *(src+i);
++j;
}
}
1) Since your iteration starts with
for(i = strlen(src); i >= 0; i--) {
it assigns 0 to the dest, thus terminating the string. So printf() prints nothing as it sees the 0 terminator. You can re-write it as:
for(i = strlen(src) - 1; i >= 0; i--) {
*(dest+j) = *(src+i);
++j;
}
dest[j] = 0; /* terminates the string */
2) You have another problem. Your dest doesn't have enough space. It should be:
char dest[strlen(src) + 1]; /* Notice the +1 */
3) You should use size_t for array indexes instead of int.
You are forgetting about the null character in your strings. dest is not large enough to hold the reverse of the string since there needs to be a null-terminator:
char dest[ strlen(src) + 1 ];
Then in the loop you are copying the null-terminator to the front of the string so it is essentially the string "" but using more memory. You need to start at the index strlen() - 1:
for(i = strlen(src)-1; i >= 0; i-- ){
The problem is with the for(i=strlen(src)..... in reverse(). src[strlen(src)] is 0 (zero; string-termination). So when you get to printf(), it sees the firs char and outputs an empty string.
Use strlen(str)-1, and then *(dest+j) = 0 after the for-loop.
In total, reverse() should be:
void reverse(char *src, char *dest) {
int i;
int j = 0;
for(i = strlen(src)-1; i >= 0; i--) {
*(dest+j) = *(src+i);
++j;
}
*(dest+j) = 0;
}

Assign value to char in array

I'm trying to brush up on my C/C++ and I seem to have forgotten how to properly manipulate char arrays.
Code:
#include <iostream>
#include <string.h>
void reverse(char* str)
{
int numChar = strlen(str);
char *reversed = (char*)malloc(sizeof(char) * (numChar + 1));
int i = numChar;
int j = 0;
while(i >= 0)
{
reversed[j] = str[i];
j++;
i--;
printf("%c", reversed[j]);
}
printf("%s", reversed);
}
int main()
{
char* str;
strcpy(str, "apple\0");
reverse(str);
return 0;
}
I'm very certain I'm not doing what I intend to with reversed[j] = str[i] as reversed comes out empty. What's the correct way to go about this?
From first glance, In main(), memory has to be allocated to character pointer str before referencing it in strcpy
int main()
{
char* str = malloc(6) or use char str[6];
// or char *str = "apple"; is sufficient, strcpy is not required in this case
strcpy(str, "apple\0");
reverse(str);
return 0;
}
Another one :
In reverse() function, you will have to increment j after printing
while(i >= 0)
{
reversed[j] = str[i];
printf("%c", reversed[j]);
j++; //Moved here
i--;
}
reversed[j] = '\0' //Null termination
printf("\n %s", reversed);
or only the below two statements would be sufficient enough to post increment j and decrement i
reversed[j] = str[i--];
printf("%c", reversed[j++]);
Since you start with i being the strlen of the input string (e.g. HAHA -> 4), you start copying at haha[4], which is the null byte at the end of the string. IOW you just null terminated your output right at the start. Try setting i to numChar - 1.
(After fixing the problem Santosh A mentioned)
And then, make sure you null terminate the result!

Implementation of strspn( )

The definition of library function strspn is:
size_t strspn(const char *str, const char *chars)
/* Return number of leading characters at the beginning of the string `str`
which are all members of string `chars`. */
e.g. if str is "fecxdy" and chars is "abcdef" then the function would return 3, since f, e and c all appear somewhere in chars, giving 3 leading characters of str, and x is the first character of str which is not a member of chars.
Could someone help me write an implementation of strspn in C. The only library function I am allowed to call from the implementation is strlen?
The basic idea is to step through the string, one character at a time, and test if it's in the character set. If it's not, stop and return the answer. In pseudocode, that would look like:
count = 0
for each character c in str
if c is not in chars
break
count++
return count
The if c is not in chars test can be implemented by iterating through all of the characters of chars and testing if c matches any of the characters. Note that this is not the fastest implementation, since it involves stepping through the chars string for each character in str. A faster implementation would use a lookup table to test if c is not in chars.
I found this question while going over old exams. You weren't allowed to use indexing or any standard functions. Here's my attempt at a solution:
#include <stdio.h>
size_t myStrspn(const char *str1, const char *str2){
size_t i,j;
i=0;
while(*(str1+i)){
j=0;
while(*(str2+j)){
if(*(str1+i) == *(str2+j)){
break; //Found a match.
}
j++;
}
if(!*(str2+j)){
return i; //No match found.
}
i++;
}
return i;
}
void main(){
char s[] = "7803 Elm St.";
int n = 0;
n = myStrspn(s,"1234567890");
printf("The number length is %d. \n",n);
}
Here's the solution from the exam:
#include<stdio.h>
size_t strspn(const char* cs, const char* ct) {
size_t n;
const char* p;
for(n=0; *cs; cs++, n++) {
for(p=ct; *p && *p != *cs; p++)
;
if (!*p)
break;
}
return n;
}
For loops made it much more compact.
I think this should be pretty fast
size_t strspn(const unsigned char *str, const unsigned char *chars){
unsigned char ta[32]={0};
size_t i;
for(i=0;chars[i];++i)
ta[chars[i]>>3]|=0x1<<(chars[i]%8);
for(i=0;((ta[str[i]>>3]>>(str[i]%8))&0x1);++i);
return i;
}
Thanks to others for sanity checks.
A naive implementation of strspn() would iterate on the first string, as long as it finds the corresponding character in the second string:
#include <string.h>
size_t strspn(const char *str, const char *chars) {
size_t i = 0;
while (str[i] && strchr(chars, str[i]))
i++;
return i;
}
Given that you are not allowed to call strchr(), here is a naive native implementation:
size_t strspn(const char *str, const char *chars) {
size_t i, j;
for (i = 0; str[i] != '\0'; i++) {
for (j = 0; chars[j] != str[i]; j++) {
if (chars[j] == '\0')
return i; // char not found, return index so far
}
}
return i; // complete string matches, return length
}
Scanning the second string repeatedly can be costly. Here is an alternative that combines different methods depending on the length of chars, assuming 8-bit bytes:
size_t strspn(const char *str, const char *chars) {
size_t i = 0;
char c = chars[0];
if (c != '\0') { // if second string is empty, return 0
if (chars[1] == '\0') {
// second string has single char, use a simple loop
while (str[i] == c)
i++;
} else {
// second string has more characters, construct a bitmap
unsigned char x, bits[256 / 8] = { 0 };
for (i = 0; (x = chars[i]) != '\0'; i++)
bits[x >> 3] |= 1 << (x & 7);
// iterate while characters are found in the bitmap
for (i = 0; (x = str[i]), (bits[x >> 3] & (1 << (x & 7))); i++)
continue;
}
}
return i;
}
int my_strspn(const char *str1,const char *str2){
int i,k,counter=0;
for(i=0;str1[i]!='\0';i++){
if(counter != i) break;
for(k=0;str2[k]!='\0';k++){
if(str1[i]==str2[k])
counter++;
}
}
return counter;
}
Create a lookup table (a poor man's set) for all possible ASCII chars, and just lookup each character in str. This is worst case O(max(N,M)), where N is the number of characters in str and M is the number of characters in chars.
#include <string.h>
size_t strspn(const char *str, const char *chars) {
int i;
char ch[256] = {0};
for (i = 0; i < strlen(chars); i++) {
ch[chars[i]] = 1;
}
for (i = 0; i < strlen(str); i++) {
if (ch[str[i]] == 0) {
break;
}
}
return i;
}
This could also be solved without using strlen at all, assuming both strings are zero-terminated. The disadvantage of this solution is that one needs 256 bytes of memory for the lookup table.
Without touching a C-compiler for the last couple of years. From the top of my head something like this should work:
int spn = 0;
while(*str++ != '\0')
{
char *hay = chars;
bool match = false;
while(*hay++ != '\0')
{
if(*hay == *str)
{
match = true;
break;
}
}
if(match)
spn++;
else
return spn;
}
return spn;
Well, implementing a standard library for my OS, here is my solution (C++).
KCSTDLIB_API_FUNC(size_t DECL_CALL strspn(const char * str1, const char * str2))
{
size_t count = 0;
auto isin = [&](char c)
{
for (size_t x = 0; str2[x]; x++)
{
if (c == str2[x])
return true;
};
return false;
};
for (; isin(str1[count]); count++);
return count;
}

Resources