What are the issues with the following code in C?
char* copyStr(const char* someStr)
{
char buf[100];
sprintf(buf, ā%sā, someStr);
return buf;
}
There are 2 major problems in your code:
sprintf(buf, ā%sā, someStr); will attempt to store more than 100 bytes to buf if the string pointed to by somStr is longer than 99 bytes plus the null terminator, causing undefined behavior. This call is equivalent to strcpy with the same issue. You should always use snprintf instead of sprintf, passing the length of the destination array, thus preventing a buffer overflow.
returning a pointer to a local array is incorrect as the object will go out of scope immediately upon exiting the function: the caller will invoke undefined behavior dereferencing this pointer.
For your purpose, you should allocate a block of memory, copy the string to it and return a pointer to it:
#include <stdlib.h>
#include <string.h>
char *copyStr(const char *someStr) {
size_t len = strlen(someStr);
char *p = malloc(len + 1);
if (p != NULL) {
memcpy(p, someStr, len + 1);
}
return p;
}
This is exactly the semantics of the function strdup defined in POSIX and standardized the upcoming C23 Standard:
char *strdup(const char *s);
Related
I am studying for a Data Structures and Algorithms exam. One of the sample questions related to dynamic memory allocation requires you to create a function that passes a string, which takes it at copies it to a user defined char pointer. The question provides the struct body to start off.
I did something like this:
typedef struct smart_string {
char *word;
int length;
} smart_string;
smart_string* create_smart_string(char *str)
{
smart_string *s = (smart_string*)malloc(sizeof(smart_string));
s->length = strlen(str);
s->word = malloc(s->length);
strcpy(s->word, str);
return s;
}
But the answer was this
typedef struct smart_string {
char *word;
int length;
} smart_string;
smart_string *create_smart_string(char *str)
{
smart_string *s = malloc(sizeof(smart_string));
s->length = strlen(str);
s->word = malloc(sizeof(char) * (s->length + 1));
strcpy(s->word, str);
return s;
}
I went on code:blocks and tested them both to see any major differences. As far as I'm aware, their outputs were the same.
I did my code the way it is because I figured if we were to allocate a specific block of memory to s->word, then it should be the same number of bytes as s ->length, because that's the string we want to copy.
However the correct answer below multiplies sizeof(char) (which is just 1 byte), with s->length + 1. Why the need to add 1 to s->length? What's the importance of multiplying s->length by sizeof(char)? What mistakes did I make in my answer that I should look out for?
sizeof(char) == 1 by definition, so that doesn't matter.
You should not cast the result of malloc: Do I cast the result of malloc?
And your only real difference is that strlen returns the length of the string, not including the terminating NUL ('\0') character, so you need to add + 1 to the size of the buffer as in the solution.
If you copy there the string, the terminating character won't be copied (or worse, it will be copied on some other memory), and therefore, any function that deals with strings (unless you use special safety functions such as strscpy) will run through the buffer and past it since they won't find the end. At that point it is undefined behaviour and everything can happen, even working as expected, but can't rely on that.
The reason it is working as expected is because probably the memory just next to the buffer will be 0 and therefore it is being interpreted as the terminating character.
Your answer is incorrect because it doesn't account for the terminating '\0'-character. In C strings are terminated by 0. That's how their length can be determined. A typical implementation of strlen() would look like
size_t strlen(char const *str)
{
for (char const *p = str; *p; ++p); // as long as p doesn't point to 0 increment p
return p - str; // the length of the string is determined by the distance of
} // the '\0'-character to the beginning of the string.
But both "solutions" are fubar, though. Why would one allocate a structure consisting of an int and a pointer on the free-store ("heap")!? smart_string::length being an int is the other wtf.
#include <stddef.h> // size_t
typedef struct smart_string_tag { // *)
char *word;
size_t length;
} smart_string_t;
#include <assert.h> // assert()
#include <string.h> // strlen(), strcpy()
#include <stdlib.h> // malloc()
smart_string_t create_smart_string(char const *str)
{
assert(str); // make sure str isn't NULL
smart_string_t new_smart_string;
new_smart_string.length = strlen(str);
new_smart_string.word = calloc(new_smart_string.length + 1, sizeof *new_smart_string.word);
if(!new_smart_string.word) {
new_smart_string.length = 0;
return new_smart_string;
}
strcpy(new_smart_string.word, str);
return new_smart_string;
}
*) Understanding C Namespaces
I have an array of char and I'm trying to have a string literal with the same chars in the array.
I tried strcpy, and try =, and I tried what I did in the following code. But it doesn't seem to work or I'm understanding something.
char s1[10]="Youssef";
char *s2
while(*s2!='\0')
*s2++=*s1++;
printf("%s",s2);
Process doesn't return.
String literals are read only.
In any case, what you are trying to do seems you are confused.
A string literal: char *sl = "string literal";
An uninitialized char pointer: char *s2;
In order to do the copy you like, you first need to allocate memory for the string.
Moreover, you cannot do pointer arithmetics with an array. Arrays and pointers are not the same thing!
Furthermore, you should remember the origin of s2 pointer, since after incrementing it until the copy is complete, you would then need to reset the pointer.. Exercise: Think what would happen if you did the copy in a function (preferably named mystrcpy`)...
Complete example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char s1[10]="Youssef";
char *s2 = malloc(sizeof(char) * (strlen(s1) + 1)); // +1 for the NULL-terminator
int i = 0;
char *origin_s2 = s2;
while(s1[i] != '\0')
*s2++ = s1[i++];
*s2 = '\0';
s2 = origin_s2;
printf("%s\n", s2);
return 0;
}
Output:
Youssef
PS: It is highly recommended to check if the dynamic allocation of the memory was successful (check if return value of malloc() is not NULL).
I am trying to read argv[1] starting from the second character until the end of the string in argv[1] (ignoring the first character, which is a flag). How can I do this?
I tried some library functions and other ways such as storing it in a variable such as
char *variable = strncpy(argv[1][1], strlen(argv[1]))
but it didn't work.
You are running up against two fundamental misconceptions regarding variables and pointers in C.
Let's start with:
char *variable = strncpy(argv[1][1], strlen(argv[1]))
The biggest problem (aside from the improper use of strncpy) is you attempt to assign the return of strncpy to char *variable where char *variable is a pointer-to-char that is uninitialized and points to no valid storage. Your attempt to assign the return fails because the proper prototype for strncpy is:
char *strncpy(char *dest, const char *src, size_t n);
(note: the dest parameter. The destination must have adequate storage to accept n characters. **further note:** if there is nonull byte` among the first n bytes of src, the array of bytes placed in dest will not be a null-terminated string.)
Now either by cleverness or happy-circumstance using the strlen of the complete argv[1] to allocate storage for dest and copying from argv[1] + 1 does provide space for the null byte.
Your next misconception is using argv[1][1] in strncpy. argv[1][1] has type char, not char*. (though your could use &argv[1][1] to use the address of argv[1][1] -- but not as you have it above.
argv[1] is a pointer of type char *. Being a pointer-to-char, if you want to skip one char, you want to read from the address pointer + 1 (or argv[1] + 1 in this case). Now it may make things easier to understand if you declare a separate pointer, e.g. char *p = argv[1]; and then use p + 1, but it is the same thing.
Putting that together, it looks like you intended:
#include <stdio.h>
#include <string.h>
int main (int argc, char **argv) {
if (argc < 2)
return 1;
size_t len = strlen (argv[1]);
char variable[len];
strcpy (variable, argv[1] + 1);
printf ("variable : %s\n", variable);
return 0;
}
Where with your example argument of +name, you would get:
Example Use/Output
$ ./bin/argv1plus1 +name
variable : name
For sake of completeness, if your compiler does not support use of a Variable Length Array (VLA) as used in char variable[len]; above, then your options are to declare variable as a fixed size array and validate that strlen(argv[1]) has no more characters than your fixed size, or, you simply allocate storage for variable dynamically by calling malloc (or calloc or realloc). A short example using malloc would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv) {
if (argc < 2)
return 1;
size_t len = strlen (argv[1]);
char *variable = malloc (len); /* allocate a block of memory len chars long */
if (variable == NULL) { /* always validate malloc succeeded */
perror ("malloc failure");
exit (EXIT_FAILURE);
}
strcpy (variable, argv[1] + 1);
printf ("variable : %s\n", variable);
free (variable); /* don't forget to free the memory you allocate */
return 0;
}
(same example & output)
Look things over and let me know if you have further questions.
I'm trying to concat two strings, supposing the "dest" string hasn't enough space to add another one, so I'm using dynamic arrays to solve it.
The problem is a mremap_chunk error when trying to compile the code.
I don't know what I'm missing since the realloc call has all the right params place in.
Error:
malloc.c:2869: mremap_chunk: Assertion `((size + offset) & (GLRO (dl_pagesize) - 1)) == 0' failed.
Aborted (core dumped)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *strcatt(char *s1, char *s2)
{
int a = strlen(s1);
int b = strlen(s2);
int i, size_ab = a+b;
s1 = (char *) realloc (s1, size_ab*sizeof(char));
for(i=0; i<b; i++) {
s1[i+a]=s2[i];
}
s1[size_ab]='\0';
return s1;
}
int main()
{
char s1[]="12345";
char s2[]="qwerty";
strcatt(s1,s2);
printf("%s\n", s1);
return 0;
}
First, you are treating non-heap memory as heap memory, don't do that.
Second you're not including space for the terminator in the calculation.
Here are some more points:
Don't name functions starting with str, that's a reserved name space.
Buffer sizes should be size_t, not int.
Don't cast the return value of malloc() in C.
Use memcpy() to copy blocks of memory when you know the size.
The "right hand side" strings should be const.
Deal with the possibility of allocation error.
I consider it bad practice to scale by sizeof (char), that's always 1.
Here's how I would write it, assuming the same logic:
char * my_strcatt(char *s1, const char *s2)
{
const size_t a = strlen(s1);
const size_t b = strlen(s2);
const size_t size_ab = a + b + 1;
s1 = realloc(s1, size_ab);
memcpy(s1 + a, s2, b + 1);
return s1;
}
You can not realloc or free a memory that is not allocated with a call to malloc or is not NULL.
From section 7.22.3.5. The realloc function in C11 draft
The realloc function deallocates the old object pointed to by ptr and
returns a pointer to a new object that has the size specified by size.
The contents of the new object shall be the same as that of the old
object prior to deallocation, up to the lesser of the new and old
sizes. Any bytes in the new object beyond the size of the old object
have indeterminate values.
So, s1 = (char *) realloc (s1, size_ab*sizeof(char)); is plainly wrong for your inputs (automatic arrays), never do that.
And then there are many more problems which can be fixed with some help from a debugger.
The clang debugger gives a very clear error description:
malloc: error for object 0x7fff6fbb16d6: pointer being realloc'd was not allocated
set a breakpoint in malloc_error_break to debug
Both of your arrays are initialized as string literals. Further on, your function tries to modify a string literal by reallocing it, which is wrong by C standard because you can't reallocate what you haven't allocated, and then copying the members of the second string literal to the "object" you intended to modify by misusing realloc() on a string literal.
The code would work if you had dynamically defined a third string in which you would have summed the contents of both:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *mystrcatt(char *s1, char *s2)
{
int a = strlen(s1);
int b = strlen(s2);
int i, size_ab = a+b;
char *s3 = malloc (size_ab*sizeof(char)); //sizeof(char) is always 1
for(i=0; i<a; i++) { //inefficient
(s3[i])=s1[i];
}
for(i=0; i<b; i++) { //inefficient
(s3[i+a])=s2[i];
}
s3[size_ab]='\0';
return s3;
}
int main()
{
char s1[]="12345";
char s2[]="qwerty";
char *s3 = mystrcatt(s1,s2);
printf("%s\n", s3);
free(s3);
return 0;
}
Please, also note that you don't cast the return of malloc() in C.
All of the code below on C.
Both the code snippet below are compiled, but the difference is that the second program crashes at startup.
One:
#include <stdio.h>
#include <string.h>
void main() {
char *foo = "foo";
char *bar = "bar";
char *str[80];;
strcpy (str, "TEXT ");
strcat (str, foo);
strcat (str, bar);
}
Two:
#include <stdio.h>
#include <string.h>
void main() {
char *foo = "foo";
char *bar = "bar";
char *str="";
strcpy (str, "TEXT ");
strcat (str, foo);
strcat (str, bar);
}
The difference is that in the first case, said the size of the str string, while the second is not. How to make string concatenation without a direct indication of the size of the string str?
The difference is that in the first case, said the size of the str
string, while the second is not.
No. In the first program, the following statement
char *str[80];
defines str to be an array of 80 pointers to characters. What you need is a character array -
char str[80];
In the second program,
char *str="";
defines str to be a pointer to the string literal "", not an array. Arrays and pointers are different types.
Now, the second program crashes because
char *str="";
defines str to be a pointer to a string literal. The first argument of strcpy should be a pointer to a buffer which is large enough for the string to be copied which is pointed to by its second argument.
However, str points to the string literal "" which is allocated in read-only memory. By passing str to strcpy, it invoked undefined behaviour because strcpy tries to modify it which is illegal. Undefined behaviour means the behaviour is unpredictable and anything can happen from program crash to due to segfault (illegal memory access) or your hard drive getting formatted. You should always avoid code which invoked undefined behaviour.
How to make string concatenation without a direct indication of the
size of the string str?
The destination string must be large enough to store the source string else strcpy will overrun the buffer pointed to by its first argument and invoke undefined behaviour due to illegal memory access. Again, the destination string must have enough space for the source string to be appended to it else strcat will overrun the buffer and again cause undefined behaviour. You should ensure against this by specifying the correct size of the string str in this case.
Change to:
One:
#include <stdio.h>
#include <string.h>
void main() {
char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy (str, "TEXT ");
strcat (str, foo);
strcat (str, bar);
}
Two:
#include <stdio.h>
#include <string.h>
void main() {
char *foo = "foo";
char *bar = "bar";
char str[80]="";
strcpy (str, "TEXT ");
strcat (str, foo);
strcat (str, bar);
}
#include <stdio.h>
#include <string.h>
void main() {
char *foo = "foo";
char *bar = "bar";
char *str=NULL;
size_t strSize = strlen(foo)+strlen(bar)+strlen("TEXT ")+1;
str=malloc(strSize);
if(NULL==str)
{
fprintf(stderr, "malloc() failed.\n");
goto CLEANUP;
}
strcpy (str, "TEXT ");
strcat (str, foo);
strcat (str, bar);
CLEANUP
if(str)
free(str);
}
size_t len1 = strlen(first);
size_t len2 = strlen(second);
char * s = malloc(len1 + len2 + 2);
memcpy(s, first, len1);
s[len1] = ' ';
memcpy(s + len1 + 1, second, len2 + 1); // includes terminating null
Did I do good?
In the second version you have set char *str=""; this is equivalent to allocating an empty string on the stack with 1 byte which contains null for end of string. Had you written char*str="0123456789", you would have allocated 11 bytes on the stack the first 10 of which would have been "0123456789" and the 11th byte would have been null. If you try to copy more than allocated bytes to the str, your program might crash. So either allocate dynamically enough memory, or statically.
Both programs are wrong, the first provokes undefined behavior exactly as the second one.
char *str[80]; is array of pointers which you pass to functions ( strcpy, strcat) as first argument, while the first argument for those functions should be char *. Possible solution for this issue to define str as char str[80];
The issue with the second program is that char *str=""; is a pointer to read only piece of memory, which can't be changed.In this case one of possible solutions can be :
char* str = malloc(80);
strcpy(str,"");
strcpy (str, "TEXT ");
strcat (str, foo);
strcat (str, bar);
You'll have to allocate memory before copying. The first defines the size so as 80
char *str = new char[10]; could also be a method of allocation. malloc function could also be used to allocate memory.
Here are some references that might help you
http://www.cplusplus.com/reference/cstdlib/malloc/
http://www.cplusplus.com/reference/cstdlib/calloc/