Here's my program:
#include <stdio.h>
char *ft_strcat(char *dest, char *src)
{
int i;
int k;
i = 0;
k = 0;
while (dest[i])
i++;
while (src[k])
{
dest[i + k] = src[k];
//i++;
k++;
}
dest[i + k] = '\0';
return (dest);
}
int main(){
//ft_strcat
char str[] = "Hello, ";
char str2[] = "World!";
printf("%s", ft_strcat(str, str2));
return 0;
}
It's implementing of strcat function.
When I'm trying to copy "World!" to "Hello, " I have an error "zsh: abort". There's no problem when I'm trying to copy to "Hello ".
What can I do with this error? Why this comma causes this problem?
When you define an array with empty dimension and initialize that with a brace-enclosed initializer list, the size of the array is determined by the supplied initializer list elements.
So, in your case, str and str2 are just long enough to hold the strings "Hello, " and "World!", respectively.
So, the problem here is, the destination buffer (passed as the first argument of ft_strcat()) has absolutely no space to hold the concatenated result. You're accessing out of bound memory, thus causing undefined behavior.
In the very first iteration of the while loop,
while (src[k])
{
dest[i + k] = src[k];
//i++;
k++;
}
the index i+k points to out of bound memory for dest. No sooner than you try to use the index to access the memory location, you face UB.
You need to make sure, the destination has enough space left to hold the concatenated result. For that, you can have either of two approaches
Define a bigger array size statically and use that as destination. You can always check for the actual size vs. already used size easily in this case, as this is a character type array meant to be used as string (hint: sizeof vs strlen()).
You can make use of a pointer, use memory allocator function to allocate certain amount of memory and realloc() as needed.
Related
I will say honestly, this isn't my code. It's my brother's who's studying with me but he's a ahead of me.
Please notice char *str and char *resultString in the function char *replaceWord().
/*Suppose you have a template letter.txt. You have to fill in values to a template. Letter.txt looks something like this:
Thanks {{name}} for purchasing {{item}} from our outlet {{outlet}}. Please visit our outlet {{outlet}} for any kind of problems. We plan to serve you again soon.
You have to write a program that will automatically fill the template.For this, read this file and replace these values:
{{name}} - Harry
{{item}} - Table Fan
{{outlet}} - Ram Laxmi fan outlet
Use file functions in c to accomplish the same.*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * replaceWord(const char * str, const char * oldWord, const char * newWord)
{
char * resultString;
int i, count = 0;
int newWordLength = strlen(newWord);
int oldWordLength = strlen(oldWord);
for (i = 0; str[i] != '\0'; i++)
{
if (strstr(&str[i], oldWord) == &str[i])
{
count++;
//Jumping over the word and continuing
i = i + oldWordLength - 1;
}
}
//dynamically allocation memory to resultString since it can be big or samll depending on the size of the newWord.
/*i = old string length , count = no. of times the word appeared in the string,
newWordLength-oldWordLength=difference between the new word and the old word
+1 for the null character '\0'
Basically we are saying that add the size required for the newWord to the strings length i.e i;
*/
resultString = (char *)malloc(i + count * (newWordLength - oldWordLength) + 1);
i = 0; //refreshing the i for the while loop
while (*str)
{
if (strstr(str, oldWord) == str)
{
strcpy(&resultString[i], newWord);
i += newWordLength;
str += oldWordLength;
}
else
{
resultString[i] = *str;
i+=1;
str+=1;
}
}
resultString[i] = '\0';
return resultString;
}
int main()
{
FILE *ptr = NULL;
FILE *ptr2 = NULL;
ptr = fopen("letter.txt", "r"); //where the template is stored
ptr2 = fopen("newLetter.txt", "w"); //where the new bill will be stored.
char str[200];
fgets(str, 200, ptr); //store the bill template in the str variable.
printf("The original bill template is : %s\n", str);
//Calling the replacing fucntion
char *newStr = str; //newStr will store the new bill i.e generated
newStr = replaceWord(str, "{{name}}", "Mary");
newStr = replaceWord(newStr, "{{item}}", "Waffle Machine");
newStr = replaceWord(newStr, "{{outlet}}", "Belgium Waffle");
printf("\nThe bill generated is:\n%s", newStr);
fprintf(ptr2, "%s", newStr);
fclose(ptr);
fclose(ptr2);
return 0;
}
Can someone explain why the pointer *str and *resultString are expressed different ways in the program and what are they doing? Sometimes it's *str, &str or str[i].
Please explain.
I know that a pointer is used to keep the address of the other variables but this code is still a mystery to me.
Also why was the function a pointer?
NOTE:"He said that's how it works" when I asked how.
Please help!! I can't focus on other things.
If you can't explain ;a link of explanation would be fine as well.
Sometimes it's *str, &str or str[i]
Those are operators.
*str
str is a poitner to a char, and having a * over it dereferences it. Meaning it fetches the value from the memory that it is pointing to. A pointer may not always point to a variable though, it can be any arbitrary memory address. But dereferencing memory that is not yours will result in Segmentation fault which is the my most beloved error that occurs almost everytime when processing arrays.
str[i]
This is the same as *(str + i). Meaning it increments the memory address by i * sizeof(<datatype of what str points to>). Then it fetches the value from that incremented address. This is used for getting elements of an array.
&str
This just given the address of the variable str, which is a pointer. So, it returns a pointer to a pointer(ie. str). A pointer to a pointer can exist.
The function is not a pointer. Instead, it returns a pointer which is *resultString. It is so that a string can be returned. The string had been initialized in this line:
resultString = (char *)malloc(i + count * (newWordLength - oldWordLength) + 1);
The comment explaining this is not complete.
//dynamically allocation memory to resultString since it can be big or samll depending on the size of the newWord.
/*i = old string length , count = no. of times the word appeared in the string,
newWordLength-oldWordLength=difference between the new word and the old word
+1 for the null character '\0'
Basically we are saying that add the size required for the newWord to the strings length i.e i;
*/
It also misses one key reason why malloc is being used instead of normal allocation. malloc allocates your variables on the heap which is shared among all functions and threads. While normal initialization would allocate it on the stack which is popped off when the function ends. So, no use after the function with the stack, so it should be used on the heap. And it is also for dynamic allocation.
tbh I thought it wouldn't be hard to learn C seeing as I already know several other languages, but I'm having trouble with my code, and I can't seem to figure out how to fix these errors. I specialize in Python, so this is much different because of all the specifications for types, pointers, etc. Anyway, here's the code below, sorry, I would paste the error, but it won't allow me to copy paste. I was using some print functions and found the error to be coming from line 9, "*returnStr += *str";. Thanks in advance for any help.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
char *multiplyString(const char *str, int num){
char *returnStr = "";
for (int i = 0; i < num; i++){
*returnStr += *str;
}
return returnStr;
}
int main(void){
bool asking = true;
int height;
const char *symbol = "#";
while (asking == true){
height = get_int("How tall should the pyramid be? pick a number between 1 and 8: ");
if (8 >= height && height >= 1){
asking = false;
}
}
for (int i=1; i<=height; i++){
printf("%s %s\n", strcat(multiplyString(" ", height-i), multiplyString(symbol, i)), multiplyString(symbol, i));
}
}
Change multiplyString() to the following
char *multiplyString(const char *str, int num) {
// + 1 for null-terminator
char *returnStr = calloc(sizeof(*returnStr), strlen(str)*num + 1);
for (int i = 0; i < num; i++) {
strcat(returnStr, str);
}
return returnStr;
}
You were attempting to modify a string literal, which is forbidden in C. Secondly, += is not string concatenation in C; rather, it was trying to perform integer addition on the first character of returnStr.
To fix this, you dynamically allocate the proper amount of memory using calloc() (which also initializes the memory to 0, which is necessary for strcat()). Then, in each iteration, append the string using strcat() to the end of the new string.
Remember to free the strings returned by this function later in the program, as they are dynamically allocated.
Two problems:
First of all, returnStr is pointing to a string literal, which is really an array of read only characters. In this case an array of only a single character, being the string terminator '\0'
Secondly, *returnStr += *str; makes no sense. It's the same as returnStr[0] = returnStr[0] + str[0]. And since the destination (returnStr[0]) is a string literal, attempting to write to it leads to undefined behavior
If you want to create a new string containing num copies of str, then you need to create a new string containing at least num * strlen(str) + 1 characters, the +1 for the terminator. Then you need to use strcat to concatenate into that new string.
Also if you allocate memory dynamically (with e.g. malloc) then you need to make sure that the first element is initialized to the string terminator.
I need help with char array. I want to create a n-lenght array and initialize its values, but after malloc() function the array is longer then n*sizeof(char), and the content of array isnt only chars which I assign... In array is few random chars and I dont know how to solve that... I need that part of code for one project for exam in school, and I have to finish by Sunday... Please help :P
#include<stdlib.h>
#include<stdio.h>
int main(){
char *text;
int n = 10;
int i;
if((text = (char*) malloc((n)*sizeof(char))) == NULL){
fprintf(stderr, "allocation error");
}
for(i = 0; i < n; i++){
//text[i] = 'A';
strcat(text,"A");
}
int test = strlen(text);
printf("\n%d\n", test);
puts(text);
free(text);
return 0;
}
Well before using strcat make
text[0]=0;
strcat expects null terminated char array for the first argument also.
From standard 7.24.3.1
#include <string.h>
char *strcat(char * restrict s1,
const char * restrict s2);
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.
How do you think strcat will know where the first string ends if you don't
put a \0 in s1.
Also don't forget to allocate an extra byte for the \0 character. Otherwise you are writing past what you have allocated for. This is again undefined behavior.
And earlier you had undefined behavior.
Note:
You should check the return value of malloc to know whether the malloc invocation was successful or not.
Casting the return value of malloc is not needed. Conversion from void* to relevant pointer is done implicitly in this case.
strlen returns size_t not int. printf("%zu",strlen(text))
To start with, you're way of using malloc in
text = (char*) malloc((n)*sizeof(char)
is not ideal. You can change that to
text = malloc(n * sizeof *text); // Don't cast and using *text is straighforward and easy.
So the statement could be
if(NULL == (text = (char*) malloc((n)*sizeof(char))){
fprintf(stderr, "allocation error");
}
But the actual problem lies in
for(i = 0; i < n; i++){
//text[i] = 'A';
strcat(text,"A");
}
The strcat documentation says
dest − This is pointer to the destination array, which should contain
a C string, and should be large enough to contain the concatenated
resulting string.
Just to point out that the above method is flawed, you just need to consider that the C string "A" actually contains two characters in it, A and the terminating \0(the null character). In this case, when i is n-2, you have out of bounds access or buffer overrun1. If you wanted to fill the entire text array with A, you could have done
for(i = 0; i < n; i++){
// Note for n length, you can store n-1 chars plus terminating null
text[i]=(n-2)==i?'A':'\0'; // n-2 because, the count starts from zero
}
//Then print the null terminated string
printf("Filled string : %s\n",text); // You're all good :-)
Note: Use a tool like valgrind to find memory leaks & out of bound memory accesses.
I was trying to get an element deleted from the middle of array and then shiffting the whole content, and I had a few difficulties because my test array wasn't "right". I fixed the problem by filling the array with a string using strcpy. The commented part above strcpy is the method I used before(which didn't work). Could somebody explain me maybe why isn't it working?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char *array;
int i=5;
array = (char*)calloc(1024, sizeof(char));
if(array == NULL){
printf("Out of memory\n");
return 0;
}
//array = "Just some test string";
strcpy(array, "Just some test string");
printf("%s\n", array);
memmove(array+i+1, array+i, strlen(array)-i);
array[i] = ',';
printf("%s\n", array);
memmove(array+i, array+i+1, strlen(array)-i);
printf("%s\n", array);
free(array);
return 0;
}
The printf is normally working on the commented part, but the memmove() makes my programm crash!
If you have this:
array = "Just some test string";
then
1) You have memory leak due to overwriting the calloc'ed pointer.
2) You are attempting to modify a string literal. That's probably why it crashes. Modifying a string literal is undefined behaviour. They are typically stored in read-only memory. Since strcpy() makes a copy of the string literal, it works as intended and this method fails.
In these statements
memmove(array+i+1, array+i, strlen(array)-i);
array[i] = ',';
there is an attempt to insert character ',' at the position 5 because variable i was initialized with 5.
So the part of the string including the terminating zero has to be shifted right starting with this position. The length of the part is
strlen( array ) - i + 1
because the terminating zero is also must be moved.
So these statements will look like
memmove( array + i + 1, array + i, strlen( array ) - i + 1 );
array[i] = ',';
Then in these statements
memmove( array + i, array + i + 1, strlen( array ) - i );
This statement is correct. Expression strlen( array ) - i is calculated like
( strlen( array ) + 1 ) - ( i + 1 )
As for this statement
array = "Just some test string";
then pointer array points to a string literal and though string literals in C have types of non-constant character arrays nevertheless they are immutable.
Any attempt to modify a string literal results in undefined behaviour of the program.
Also if before this statement you also initialized this pointer with the result of a call to calloc then there will be a memory leak because the pointer is reassigned and the address of the allocated memory is lost.
I tried to do something like this.
I solved it by having:
*(array+strlen(string)+1)=0;
Null terminating the string, but if the subsequent printf() is working correctly this won't help.
Otherwise see:
http://www.java-samples.com/showtutorial.php?tutorialid=591
I'm currently trying to make a program in c which will return a pointer to an array of 2 strings. The first is the characters of the string s that are in the odd position and the second are the characters in the even position. I'm not experienced in C so I need a bit of help with this program. I've been trying to code using what I know from python and java but it doesn't seem to follow the same principles with pointers. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **parity_strings(const char *s){
char dest[malloc((char)sizeof(s)/2 + 1)][malloc((char)sizeof(s)/2 + 1)]; //trying to allocate memory to an array of size 2 which will hold 2 strings.
int i;
for(i = 0; i < sizeof(s); i+= 2){ //iterating through odd strings
s[0] += dest[i];
}
for(i= 2; i< sizeof(s); i += 2){ //iterating through even strings (I suppose i could have just appended using 1 for loop but oh well
s[1] += dest[i];
}
return dest;
}
int main(int argc, char **argv) {
char **r = parity_strings(argv[1]);
printf("%s %s %s\n", r[0], r[1], argv[1]);
return 0;
}
memory allocation is just a pain too...I have no clue if it's doing what I intend on it doing. I'm trying to allocate the size of the string in bytes + 1 byte into each index of the array Dest.
any ideas on how to fix this? Thanks.
This line will not do anything good:
char dest[malloc((char)sizeof(s)/2 + 1)][malloc((char)sizeof(s)/2 + 1)];
malloc returns a pointer to the newly allocated memory. In your line above, the square brackets in dest[][] need unsigned integers. Pointers can be casted to integers, but that isn’t what you want there at all. It might compile, but it probably won’t run, and certainly won’t do what you want.
Also, sizeof(s) returns the size of the pointer to s, not the length of the string. Strings in C are really just null-terminated arrays of chars, and arrays are passed to functions with a pointer, not their entire contents. To get the length of a string, use strlen(s) instead.
You could do something like this:
char *destodd = malloc((strlen(s)/2 + 2));
char *desteven = malloc((strlen(s)/2 + 2));
char **dest = malloc(sizeof(char *) * 2);
dest[0] = desteven;
dest[1] = destodd;
I changed your + 1 above to +2. A string of length 3 needs 3 characters in destodd: one for character 1, one for character 3, and one for the NUL terminator.
It’s tricky to malloc a multi-dimensional array in C. A one-dimensional array, on the other hand, is easy. Just treat destodd and desteven like they’re arrays, even though they’re really pointers:
for (i = 0; i < strlen(s); i += 2){
desteven[i] = 'a'; // Fix this
destodd[i] = 'b';
}
The code in your for loops didn’t look like it would work. It looks like you may have been trying to use += to concatenate strings, but it only does addition of numbers. I couldn’t quickly figure out what you should set in the for loop, so 'a' and 'b' are just placeholders.
You have a few issues. As your compiler should tell you, char dest[malloc()] requires a pointer-to-unsigned cast, which is legal but is not what you want. More importantly, returning a pointer to an array allocated on the stack results in undefined behavior if you dereference the pointer, because the compiler may have already deallocated the memory. I'm not exactly sure what the intended output of the function is, but in terms of filling two char arrays, in my opinion the easiest way to do it is this:
char **parity_strings(char* buf) //Please avoid single letter variable names for anything but loop control
{
size_t buflen = strlen(buf);
if (NULL == char** dest = malloc(2 * sizeof(*dest)))
;//handle memory allocation error
if (NULL == dest[0] = malloc(buflen * sizeof(*buf)))
;//handle memory allocation error
if (NULL == dest[1] = malloc(buflen * sizeof(*buf)))
;//handle memory allocation error
//Note that you would do the above two lines in a loop for a variable sized multidimensional array
strncpy(dest[0], buf, 500);
strncpy(dest[1], buf, 500); //If you need strings larger than 500 change as necessary, mostly only needed if you are taking input from someone else but it's good practice to use strncpy over strcpy)
return dest;
}