I'm trying to build a string that consists of two variables that are divided by a null terminator. It must be done this way for the custom protocol we're using.
const char* vendorChar = "3333-3333-4444-aaa3-3333";
const char* userChar = "someUsername";
char usernameString[strlen(vendorChar) + strlen(userChar) + 1];
char* uPtr = usernameString;
strcpy(uPtr, vendorChar);
strcpy(uPtr+strlen(vendorChar)+1, userChar);
When I run the above code, it only sends over the value of vendorChar and ignores userChar. When it is working it should look like
4444-2222-3333-1111\0someUsername
So far I've learned that str functions will drop the null as it sees it at the end of the string. I think I have to use memcpy to preserve it, but I can't figure out how to.
You're right in your assumption, strcpy may copy up to the middle null char,
according to this, strcpy(char *str1, const char *str2) does this:
Copies the string pointed to by str2 to str1. Copies up to and including the null character of str2. If str1 and str2 overlap the behavior is undefined.
memcpy should solve the problem as it just treats the memory as a chunk of bytes, not as a string.
strcpy signature:
char *strcpy(char *dest, const char *src);
memcpy signature:
void *memcpy(void *dest, const void *src, size_t n);
So just replace the name and add the cumulative lengths (with both null chars of course).
EDIT
To eliviate some doubts raised here, consider this code:
#include "string.h"
#include "stdio.h"
int main() {
char x[10] = {0};
char y[10];
char z[10];
x[0] = x[1] = x[5] = 'a';
memcpy(y,x,10);
strcpy(z,x);
printf ("y[5]= %s\n", &y[5]);
printf ("z[5]= %s\n", &z[5]);
return 0;
}
results is:
y[5]= a
z[5]=
So it's clear that memcpy moved the entire length, including byte [5], while strcpy did not, stopping at the null termination
const char* vendorChar = [vendorId cStringUsingEncoding: [NSString defaultCStringEncoding]];
const char* userChar = [dsUsername cStringUsingEncoding: [NSString defaultCStringEncoding]];
char usernameString[strlen(vendorChar) + strlen(userChar) + 2];
char* uPtr = usernameString;
strcpy(uPtr, vendorChar);
memcpy(uPtr+strlen(vendorChar)+1, userChar, strlen(userChar) + 1);
Changes: I added space for the trailing \0, the userChar string is copied with trailing \0 included.
Firstly, you need usernameString[] to be like -
char usernameString[strlen(vendorChar) + strlen(userChar) + 3];
because 1 is for putting "\", 1 for "0" and the other 1 is for null terminator, so becomes 3. Then use strcat() create the string you need. Like, below:
strcpy(uPtr, vendorChar);
strcat(uPtr,"\\");
strcat(uPtr,"0");
strcat(uPtr, userChar);
Related
I am trying to create my own string copy function in C.
Copying the text works, however extra characters are added to the destination string at the end and I don't know why.
I would be very happy to get an answer.
Here is my code:
#include <stdio.h>
#include <string.h>
void copy(char *dst, char *src) {
int src_len = strlen(src);
char *src_strtng = &src[0];
char *dst_strtng = &dst[0];
for (int i = 0; i < src_len; i++) {
*dst_strtng++ = *src_strtng++;
}
printf("%s\n", dst);
}
int main() {
char srcString[] = "We promptly judged antique ivory buckles for the next prize!";
char dstString[strlen(srcString)];
copy(dstString, srcString);
}
create my own str copy function in C.
Missing null character termination
This is OP's key issue, lack of appending a null character to dst.
Only need to traverse src once
Rather than strlen() in copy(), just look for '\0'. *1
Does not return a char *
Save the destination pointer.
Prints output, yet strcpy() does not print anything.
Missing const for data referenced by src
Pedantic concern: str should act as if char was unsigned char *2
char *copy(char* dst, const char* src) {
unsigned char *udst = (unsigned char *) dst;
const unsigned char *usrc = (const unsigned char *) src;
do {
*udst++ = *usrc;
} while (*usrc++);
return dst;
}
Allocate enough space for the length of a string and a null character
strlen() returns the length of a string. That does not include the final null character.
int main() {
char srcString[] = "We promptly judged ...";
char dstString[strlen(srcString) + 1]; // Add 1
copy(dstString,srcString);
puts(dstString); // Print here, not in copy().
}
*1 C's strcpy() is char *strcpy(char * restrict s1, const char * restrict s2);
Note the restrict. This implies access via the pointer is not affected by other code. E.g. source string and destination will not overlap.
// Better as
char *copy(char* restrict dst, const char* restrict src) {
OP's use of strlen() in copy() is a good first step to handle overlapping memory (see following), but strcpy() does not need to handle that - suggest copy() not deal with overlap.
char *copy_handle_overlap(char* dst, const char* src) {
return memmove(dst, src, strlen(src) + 1);
}
*2 C string functions have:
For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value).
This is important for the rare non-2's complement to distinguish +0 from -0.
Non-2's complement support expected to be dropped with C2X.
char *
STRCAT(char *dest, const char *src)
{
strcpy(dest + strlen(dest), src);
return dest;
}
what's the meaning of the code :dest + strlen(dest)?
and when I use the code like below:
#include <stdio.h>
#include <string.h>
void main()
{
char s[10]= "123456789";
char str[10] = " 123456789";
strcat(s,str);
printf("%s\n",s);
printf("%d",sizeof(s));
}
why the string s didn't overflow and the sizeof(s) did not change?
what's the meaning of the code :dest + strlen(dest)
It calculates the pointer to the end of the dest because with strcat you want to append the second string to the end of the first. It similar to:
size_t l = strlen(dest);
char *p = &dest[l]; // dest + l pointer arithmetic.
strcpy(p, src);
why the string s didn't overflow and the sizeof(s) did not change?
s is overflowing, because after strcat is done, your string is now 20 characters long, while it holds only room for 10 characters. This invokes undefined behavior.
sizeof doesn't change, because it is determined at compile time, so it will always show the same value.
strcat and STRCAT are two different functions.:)
I think you mean the function name strcat instead of STRCAT in this declaration
char *
strcat(char *dest, const char *src)
{
strcpy(dest + strlen(dest), src);
return dest;
}
This function is designed to deal with strings that is with sequences of characters terminated by the zero character '\0'.
The function strlen returns the number of characters in a string before the terminating zero character '\0';.
So the expression dest + strlen(dest) points to the terminating zero character '\0' of the string contained in the destination character array. Thus the function strcat can append the source string to the string stored in the destination array starting from the terminating zero.
So for example if you have a character array declared like
char dest[3] = { '1', '0' };
and the source array declared like
char src[2] = { '2', '0' };
For the array dest the function strlen( dest ) will return the value 1.
As a result dest + strlen( dest ) points to the second character of the array dest that is the zero character '0'. And this call
strcpy(dest + strlen(dest), src);
will copy characters of the string stored in the array src in the array dest starting with this position and you will get the following content of the array dest
{ '1', '2', '\0' }
In your program the expression sizeof( s ) gives the number of elements with which the array s is declared
char s[10]= "123456789"
that is 10. This value specified in the declaration of the array does not depend on the content that the array will have and is calculated at the compile time.
Pay attention to that values returned by the operator sizeof have the type size_t. So to output them using the function printf you have to use the conversion specifier zu. For example
printf("%zu\n",sizeof(s));
In your program the array str
char str[10] = " 123456789";
does not contain a string because it does not have a space to accommodate the (eleventh ) terminating zero character of the string literal used as an initializer.
On the other hand, the array s does not have a space to be able to append another string to its tail.
So your program has undefined behavior.
A valid program can look like
#include <stdio.h>
#include <string.h>
int main( void )
{
char str[] = " 123456789";
char s[10 + sizeof( str ) - 1] = "123456789";
strcat( s, str );
printf( "%s\n", s);
printf( "%zu\n", strlen( s ) );
printf( "%zu\n", sizeof( s ) );
}
Pay attention to that according to the C Standard the function main without parameters shall be declared like
int main( void )
I have to create a copy of some elements of the standard library in C and I have to create a copy of strcat. So I have to create a function that concatenate two strings in C. I know arrays in C can't change the allocated size. The only fonction i'm allowed to use is copies i made of strlen, strstr, and write() ... My code looks like this :
char *my_strcat(char *dest, char *src)
{
int dest_size;
int src_size;
int current_pos;
int free_space;
int pos_in_src;
src_size = my_strlen(src);
dest_size = my_strlen(dest);
while (dest[current_pos] != '\0')
current_pos = current_pos + 1;
free_space = dest_size - current_pos;
if (free_space < src_size)
return (0);
while (src[pos_in_src] != '\0')
{
dest[current_pos] = src[pos_in_src];
pos_in_src = pos_in_src + 1;
current_pos = current_pos + 1;
}
return (dest);
}
But I don't know how to declare my dest and src in the main.
I don't know how to create an array with a big size, declare it as a string like dest = "Hello\0" but this array has to still contains more than 6 characters.
Can you help me please ?
char dest[19] = "epite";
char *src = "chor42spotted";
my_strcat(dest, src);
Also, read the man for strcat(3)
the dest string must have enough space for the result.
https://linux.die.net/man/3/strcat
So your function is behaving incorrectly, you do not need to check that you have enough free space in dest
You want a function mystrcat which behaves exactly like stdlib strcat.
So the prototype is
/*
concatenate src to dest
dest [in / out] - the string to add to (buffer must be large enough)
src [in] - the string to concatenate.
Returns: dest (useless little detail for historical reasons).
*/
char *mystrcat(char *dest, const char *src);
Now we call it like this
int main(void)
{
char buff[1024]; // nice big buffer */
strcpy(buff, "Hello ");
mystrcat(buff, "world");
/* print the output to test it */
printf("%s\n", buff);
return 0;
}
But I'm not going to write mystrcat for you. That would make your homework exercise pointless.
The 1st parameter of the array simply has to be large enough to contain both strings + one null terminator. So if you for example have "hello" and "world", you need 5 + 5 +1 = 11 characters. Example:
#define LARGE_ENOUGH 11
int main (void)
{
char str[LARGE_ENOUGH] = "hello";
my_strcat(str, "world");
puts(str); // gives "helloworld"
}
In real world applications, you would typically allocate space for the array to either be same large number (couple of hundred bytes) or with a length based on strlen calls.
As for the implementation itself, your solution is needlessly complicated. Please note that the real strcat leaves all error checking to the caller. It is most likely implemented like this:
char* strcat (char* restrict s1, const char* restrict s2)
{
return strcpy(&s1[strlen(s1)], s2);
}
The most important part here is to note the const-correctness of the s2 parameter.
The restrict keywords are just micro-optimizations from the C standard, that tells the compiler that it can assume that the pointers point at different memory areas.
If you wish to roll out your own version with no library function calls just for fun, it is still rather easy, you just need two loops. Something like this perhaps:
char* lolcat (char* restrict s1, const char* restrict s2)
{
char* s1_end = s1;
while(*s1_end != '\0') // find the end of s1
{
s1_end++;
}
do // overwrite the end of s1 including null terminator
{
*s1_end = *s2;
s1_end++;
s2++;
} while(*s1_end != '\0'); // loop until the null term from s2 is copied
return s1;
}
I tried to code a function which replace all string s1 to s2, in a given string s.
however, i don't know why my program stop at the line *p=0 in that replace function without any error reported? ##
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void replace(char * s, char * s1, char * s2) {
char * p; int l=strlen(s2);
while ((p=strstr(s,s1))) {
*p=0;
p+=l;
strcat(s,s2);
strcat(s,p);
}
}
int main(void) {
char *s=(char *)"cmd=ls+-la&abc=xyz";
replace (s, "+", " ");
printf("%s", s);
return EXIT_SUCCESS;
}
There are some problems with the replace function but, first of all, there is a big difference between a pointer to a constant char array vs a character array:
char *str = "some string";
Assigns str the address of the immutable character array (read-only), it does not copy the string, only pointers are involved. Any attempt to modify that string will result in undefined behavior.
char str[] = "some string";
In this case str is an array (of size big enough to hold the string + \0) that is initialized to that string, allowing the modification of individual characters within the array.
Back to your replace function.
I will start with the first thing that I saw which is your use of strstr and strcat inside the loop is highly inefficient. Every time you call strstr it starts from the beginning of the string and searches for the first occurrence of the second string all over, the same problem can be seen with strcat which needs to find the null-terminator every time.
Another issue I see is if the replacement string (s2) is longer than the original string (s1) you must shift the entire string to accommodate for the additional characters of the new string. The same issue will occur if the replacement string is shorter.
a basic method to replace a simple char might look like this:
while (*s)
{
if (*s == c1)
*s = c2;
++s;
}
a little more complex method to replace a string would be:
/* PRECONDITION: strlen(s1) == strlen(s2) */
int l = strlen(s2);
while (*s)
{
if (!strncmp(s, s1, l))
{
memcpy(s, s2, l);
s += l;
}
else
++s;
}
Your compiler is allowed to place string literals into read-only memory, which is probably what it did with s.
Try:
char s[] = "cmd=ls+-la&abc=xyz";
This changes s from a pointer to a string literal into an array initialized with your string.
I'm trying to learn C programming and spent some time practicing with pointers this morning, by writing a little function to replace the lowercase characters in a string to their uppercase counterparts. This is what I got:
#include <stdio.h>
#include <string.h>
char *to_upper(char *src);
int main(void) {
char *a = "hello world";
printf("String at %p is \"%s\"\n", a, a);
printf("Uppercase becomes \"%s\"\n", to_upper(a));
printf("Uppercase becomes \"%s\"\n", to_upper(a));
return 0;
}
char *to_upper(char *src) {
char *dest;
int i;
for (i=0;i<strlen(src);i++) {
if ( 71 < *(src + i) && 123 > *(src + i)){
*(dest+i) = *(src + i) ^ 32;
} else {
*(dest+i) = *(src + i);
}
}
return dest;
}
This runs fine and prints exactly what it should (including the repetition of the "HELLO WORLD" line), but afterwards ends in a Segmentation fault. What I can't understand is that the function is clearly compiling, executing and returning successfully, and the flow in main continues. So is the Segmentation fault happening at return 0?
dest is uninitialised in your to_upper() function. So, you're overwriting some random part of memory when you do that, and evidently that causes your program to crash as you try to return from main().
If you want to modify the value in place, initialise dest:
char *dest = src;
If you want to make a copy of the value, try:
char *dest = strdup(src);
If you do this, you will need to make sure somebody calls free() on the pointer returned by to_upper() (unless you don't care about memory leaks).
Like everyone else has pointed out, the problem is that dest hasn't been initialized and is pointing to a random location that contains something important. You have several choices of how to deal with this:
Allocate the dest buffer dynamically and return that pointer value, which the caller is responsible for freeing;
Assign dest to point to src and modify the value in place (in which case you'll have to change the declaration of a in main() from char *a = "hello world"; to char a[] = "hello world";, otherwise you'll be trying to modify the contents of a string literal, which is undefined);
Pass the destination buffer as a separate argument.
Option 1 -- allocate the target buffer dynamically:
char *to_upper(char *src)
{
char *dest = malloc(strlen(src) + 1);
...
}
Option 2 -- have dest point to src and modify the string in place:
int main(void)
{
char a[] = "hello world";
...
}
char *to_upper(char *src)
{
char *dest = src;
...
}
Option 3 -- have main() pass the target buffer as an argument:
int main(void)
{
char *a = "hello world";
char *b = malloc(strlen(a) + 1); // or char b[12];
...
printf("Uppercase becomes %s\n", to_upper(a,b));
...
free(b); // omit if b is statically allocated
return 0;
}
char *to_upper(char *src, char *dest)
{
...
return dest;
}
Of the three, I prefer the third option; you're not modifying the input (so it doesn't matter whether a is an array of char or a pointer to a string literal) and you're not splitting memory management responsibilities between functions (i.e., main() is solely responsible for allocating and freeing the destination buffer).
I realize you're trying to familiarize yourself with how pointers work and some other low-level details, but bear in mind that a[i] is easier to read and follow than *(a+i). Also, there are number of functions in the standard library such as islower() and toupper() that don't rely on specific encodings (such as ASCII):
#include <ctype.h>
...
if (islower(src[i])
dest[i] = toupper(src[i]);
As others have said, your problem is not allocating enough space for dest. There is another, more subtle problem with your code.
To convert to uppercase, you are testing a given char to see if it lies between 71 ans 123, and if it does, you xor the value with 32. This assumes ASCII encoding of characters. ASCII is the most widely used encoding, but it is not the only one.
It is better to write code that works for every type of encoding. If we were sure that 'a', 'b', ..., 'z', and 'A', 'B', ..., 'Z', are contiguous, then we could calculate the offset from the lowercase letters to the uppercase ones and use that to change case:
/* WARNING: WRONG CODE */
if (c >= 'a' && c <= 'z') c = c + 'A' - 'a';
But unfortunately, there is no such guarantee given by the C standard. In fact EBCDIC encoding is an example.
So, to convert to uppercase, you can either do it the easy way:
#include <ctype.h>
int d = toupper(c);
or, roll your own:
/* Untested, modifies it in-place */
char *to_upper(char *src)
{
static const char *lower = "abcdefghijklmnopqrstuvwxyz";
static const char *upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static size_t n = strlen(lower);
size_t i;
size_t m = strlen(src);
for (i=0; i < m; ++i) {
char *tmp;
while ((tmp = strchr(lower, src[i])) != NULL) {
src[i] = upper[tmp-lower];
}
}
}
The advantage of toupper() is that it checks the current locale to convert characters to upper case. This may make æ to Æ for example, which is usually the correct thing to do. Note: I use only English and Hindi characters myself, so I could be wrong about my particular example!
As noted by others, your problem is that char *dest is uninitialized. You can modify src's memory in place, as Greg Hewgill suggests, or you can use malloc to reserve some:
char *dest = (char *)malloc(strlen(src) + 1);
Note that the use of strdup suggested by Greg performs this call to malloc under the covers. The '+ 1' is to reserve space for the null terminator, '\0', which you should also be copying from src to dest. (Your current example only goes up to strlen, which does not include the null terminator.) Can I suggest that you add a line like this after your loop?
*(dest + i) = 0;
This will correctly terminate the string. Note that this only applies if you choose to go the malloc route. Modifying the memory in place or using strdup will take care of this problem for you. I'm just pointing it out because you mentioned you were trying to learn.
Hope this helps.