Why is updating one variable also updating the variable assigned to it? - c

I am getting a string from the user and storing it in a variable plaintext, and then I want to convert that string to lowercase in a for loop and store it in a separate variable plaintextLowercase. However, when I change a character in the plaintextLowercase variable to lowercase, the same happens in the plaintext variable, which I want to remain unchanged. My code is below.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
// get_string() is a function defined in the header file cs50.h that prompts the user for string input and returns that string input
string plaintext = get_string("plaintext: ");
string plaintextLowercase = plaintext;
//this converts the entire string to lowercase, but the issue is it is doing the same to the 'plaintext' variable as well
//I only want the 'plaintextLowercase' string to be lowercase, while the 'plaintext' variable
for (int i = 0, n = strlen(plaintext); i < n; i++)
{
plaintextLowercase[i] = tolower(plaintextLowercase[i]);
}
printf("%s\n", plaintext);
printf("%s\n", plaintextLowercase);
}

You are using a library that obfuscates basic C concepts behind useless typedefs and macros. If you are working with strings in C, then char * is the only right way to do it. Avoid using something else, specially if you are still learning how the language works. If your book/library/course is suggesting you to use opaque data types like CS50's string, then throw it away and go find something else to study on.
What is happening here is that your cs50.h header defines string as:
typedef char * string;
Therefore, you are wrongly given the impression that doing:
string a = "123";
string b = a;
Will somehow magically create a copy of the string. This is not the case, as the code is equivalent to:
char *a = "123";
char *b = a;
Both a and b are pointers, and will simply end up pointing to the same constant string literal in memory. No copy happens.
This is the same with the result of the get_string() function, which dynamically allocates a string for you with malloc() under the hood. So this piece of code:
string plaintext = get_string("plaintext: ");
string plaintextLowercase = plaintext;
has the same "issue". Your lowercase and plaintextLowercase are just two pointers to the same memory area, containing the same string. If you want to copy the string, you can use strdup():
string plaintext = get_string("plaintext: ");
string plaintextLowercase = strdup(plaintext);
And of course, since the new string is also dynamically allocated, do not forget to free() it when you don't need it anymore:
free(plaintextLowercase);
The string allocated by get_string() is automatically deallocated for you at exit by the library (yet another counter-intuitive CS50 thing).

Related

C String Length using null

I know the C language has dynamic length strings whereby it uses the special character null (represented as 0) to terminate a string - rather than maintaining the length.
I have this simple C code that creates a string with the null character in the fifth index:
#include <stdio.h>
#include <stdlib.h>
int main () {
char * s= "sdfsd\0sfdfsd";
printf("%s",s);
s[5]='3';
printf("%s",s);
return 0;
}
Thus, a print of the string will only output up to the fifth index. Then the code changes the character at the fifth index to a '3'. Given my understanding, I assumed it would print the full string with the 3 instead of the null, as such:
sdfsdsdfsd3sfdfsd
but instead it outputs:
sdfsdsdfsd
Can someone explain this?
This program exhibits undefined behavior because you modify a read-only string literal. char* s = "..." makes s point to constant memory; C++ actually disallows pointing non-const char* to string literals, but in C it's still possible, and we have to be careful (see this SO answer for more details and a C99 standards quote)
Change the assignment line to:
char s[] = "sdfsd\0sfdfsd";
Which creates an array on the stack and copies the string to it, as an initializer. In this case modifying s[5] is valid and you get the result you expect.
String literals can not be changed because the compiler put the string literals into a read-only data-section (but this might vary by underlying platform). The effect of attempting to modify a string literal is undefined.
In your code:
char * s= "sdfsd\0sfdfsd"
Here, s is char pointer pointing to a string "sdfsd\0sfdfsd" stored in read-only memory, making it immutable.
Here you are trying to modify the content of read-only memory:
s[5]='3';
which leads to undefined behavior.
Instead, you can use char[]:
#include <stdio.h>
int main () {
char a[] = "sdfsd\0sfdfsd";
char * s = a;
printf("%s",s);
s[5]='3';
printf("%s\n",s);
return 0;
}
This operation has failed:
s[5] = 3;
You're trying to change a string literal, which is always read-only. My testing shows the program exited with segfault:
Segmentation fault (core dumped)
You should store it in an array (or allocated memory) before any attempts to change it:
char s[] = "sdfsd\0sfdfsd";
With the above change, the program works as intended.
#include <stdio.h>
int main(){
char x[10] = "aa\0a";
x[2] = '1';
puts(x);
printf("\n\n\nPress any key to exit...");
getch();
return 0;
}
Output: aa1a

String concatenation without altering original values - C

I am making a dynamic PUT request through the use of string concatenation with C. My problem is that after the first request, a string that I need to remain static putEndpoint, is altered with the string concatenation I'm using it for.
char putEndpoint[] = "PUT /api/v1/products/";
char http[] = " HTTP/1.1";
char productID[idLen];
for(int i = 0; i < 13; i++) {
productID[i] = newTag[i];
}
// going into this strcat, putEndpoint correctly = "PUT /api/v1/products/"
char *putRequestID = strcat(putEndpoint,productID);
// putEndpoint now = "PUT /api/v1/products/xxxxxxxxxxx"
char *putRequestEndpoint = strcat(putRequestID,http);
Now if I were to make a 2nd call (which I will need to do), putEndpoint initializes as "PUT /api/v1/products/xxxxxxxxxxx".
EDIT: Is there an alternative to strcat() that could accomplish this concatenation?
I now understand that strcat() is meant to alter values.
You can use sprintf.
A simple working example-
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char putEndpoint[] = "PUT /api/v1/products/";
char http[] = " HTTP/1.1";
char *putRequestEndpoint;
putRequestEndpoint=malloc(strlen(putEndpoint)+strlen(http)+1); //allocating memory
sprintf(putRequestEndpoint,"%s%s",putEndpoint,http); // formatted output is stored in putRequestEndpoint
printf("%s\n",putRequestEndpoint);
printf("%s",putEndpoint); // will print original string unchanged.
free(putRequestEndpoint); //freeing memory
return 0;
}
I ended up changing putEndpoint to a constant and created a buffer array and that I then copied putEndpoint into. This array then resets after each request.
const char putEndpoint[] = "PUT /api/v1/products/";
char http[] = " HTTP/1.1";
char productID[idLen];
char putRequestBuffer[100];
strcpy(putRequestBuffer, putEndpoint);
strcat(putRequestBuffer, productID);
char *putRequestEndpoint = strcat(putRequestBuffer,http);
memcpy and static arrays with a fixed size and proper bounds checking are your friend, my friend.
You would have to reset it with code. If you change putEndpoint[] to a const, which will make your first line look like
const char putEndpoint[] = "PUT /api/v1/products/";
the compiler will give you an error the first time you strcat(putEndpoint,...) because strcat will be trying to write to the constant variable. This will force you to find an alternative solution.
The code below cleanly allocates temporary memory for the endpoint string as needed, copies the first string into it, concatenates the next two onto it, uses it, and finally de-allocates it and sets the pointer back to NULL.
int lengthEndpoint = 0;
char* putRequestEndpoint = NULL;
lengthEndpoint = strlen(putEndpoint) + strlen(productID) + strlen(http);
lengthEndpoint += 1; // add room for null terminator
putRequestEndpoint = malloc(lengthEndpoint);
strcpy(putRequestEndpoint, putEndpoint);
strcat(putRequestEndpoint, productID);
strcat(putRequestEndpoint, http);
// do something with putRequestEndpoint
free(putRequestEndpoint);
putRequestEndpoint = NULL;
Before answering this, I refreshed my memory of C string manipulation using this WIKIBOOKS site. I'd recommend it for further reading on this subject.

difference between string pointer and string array

I was writing code to reinforce my knowledge, I got segmentation fault. So, I also got that I have to restock(completing imperfect knowledge) on my knowledge. The problem is about strtok(). When I run the first code there is no problem, but in second, I get segmantation fault. What is my "imperfect knowledge" ? Thank you for your appreciated answers.
First code
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "team_name=fenerbahce";
char *token;
token = strtok(str,"=");
while(token != NULL)
{
printf("%s\n",token);
token = strtok(NULL,"=");
}
return 0;
}
Second code
#include <stdio.h>
#include <string.h>
int main() {
char *str= "team_name=fenerbahce";
char *token;
token = strtok(str,"=");
while(token != NULL)
{
printf("%s\n",token);
token = strtok(NULL,"=");
}
return 0;
}
From strtok -
This function is destructive: it writes the '\0' characters in the elements of the string str. In particular, a string literal cannot be used as the first argument of strtok.
And in the second case, str is a string literal which resides in read only memory. Any attempt to modify string literals lead to undefined behavior.
You see string literals are the strings you write in "". For every such string, no-matter where it is used, automatically a global space is alloacted to store it. When you assign it to an array - you copy it's content into a new memory, that of the array. Otherwise you just store a pointer to it's global memory storage.
So this:
int main()
{
const char *str= "team_name=fenerbahce";
}
Is equal to:
const char __unnamed_string[] { 't', 'e', /*...*/, '\0' };
int main()
{
const char *str= __unnamed_string;
}
And when assigning the string to array, like this:
int main()
{
char str[] = "team_name=fenerbahce";
}
To this:
const char __unnamed_string[] { 't', 'e', /*...*/, '\0' };
int main()
{
char str[sizeof(__unnamed_string) / sizeof(char)];
for(size_t i(0); i < sizeof(__unnamed_string) / sizeof(char); ++i)
str[i] = __unnamed_string[i];
}
As you can see there is a difference. In the first case you're just storing a single pointer and in the second - you're copying the whole string into local.
Note: String literals are un-editable so you should store their address at a constant.
In N4296 - § 2.13.5 .8 states:
Ordinary string literals and UTF-8 string literals are also referred
to as narrow string literals. A narrow string literal has type “array
of n const char”, where n is the size of the string as defined below,
and has static storage duration
The reason behind this decision is probably because this way, such arrays can be stored in read-only segments and thus optimize the program somehow. For more info about this decision see.
Note1:
In N4296 - § 2.13.5 .16 states:
Evaluating a string-literal results in a string literal object with
static storage duration, initialized from the given characters as
specified above.
Which means exactly what I said - for every string-literal an unnamed global object is created with their content.
char *str= "team_name=fenerbahce";
char str[]= "team_name=fenerbahce";
The "imperfect" knowledge is about the difference between arrays and pointers! It's about the memory you cannot modify when you create a string using a pointer.
When you create a string you allocate some memory that will store those values (the characters of the string). In the next lines I will refer to this when I'll talk about the "memory allocated at the start".
When you create a string using an array you will create an array that will contain the same characters as the ones of the string. So you will allocate more memory.
When you create a string using a pointer you will point to the address of memory that contains that string (the one allocated at the start).
You have to assume that the memory created at the start is not writable (that's why you'll have undefined behavior, which means segmentation fault most of the times so don't do it).
Instead, when you create the array, that memory will be writable! That's why you can modify with a command like strtok only in this case

initialization makes integer from pointer without a cast [enabled by default]

i just started to learn how to program and i encountered this error that goes like this: "initialization makes integer from pointer without a cast [enabled by default]"
What is the problem?
// This program pairs three kids with their favorite superhero
#include <stdio.h>
#include <string.h>
main()
{
char Kid1[12];
// Kid1 can hold an 11-character name
// Kid2 will be 7 characters (Maddie plus null 0)
char Kid2[] = "Maddie";
// Kid3 is also 7 characters, but specifically defined
char Kid3[7] = "Andrew";
// Hero1 will be 7 characters (adding null 0!)
char Hero1 = "Batman";
// Hero2 will have extra room just in case
char Hero2[34] = "Spiderman";
char Hero3[25];
Kid1[0] = 'K'; //Kid1 is being defined character-by-character
Kid1[1] = 'a'; //Not efficient, but it does work
Kid1[2] = 't';
Kid1[3] = 'i';
Kid1[4] = 'e';
Kid1[5] = '\0'; // Never forget the null 0 so C knows when the
// string ends
strcpy(Hero3, "The Incredible Hulk");
printf("%s\'s favorite hero is %s.\n", Kid1, Hero1);
printf("%s\'s favorite hero is %s.\n", Kid2, Hero2);
printf("%s\'s favorite hero is %s.\n", Kid3, Hero3);
return 0;
}
The problem is with char Hero1 = "Batman":
When you use a double-quoted string of characters in your code, the compiler replaces it with a pointer to the beginning of the memory space in which the string will reside during runtime.
So by char Hero1 = "Batman", you are actually attempting to assign a memory address (which typically consists of 32 or 64 bits of data, depending on your system) into a character variable (which typically stores 8 bits of data).
In order to fix the problem, you need to change it to either one of the following options:
char Hero1[] = "Batman"
char* Hero1 = "Batman"
FYI, in both cases above, the string "Batman" will reside in a read-only memory section during runtime.
However, there is a notable difference between these two cases:
Using char Hero1[], the "Batman" string will be copied into the stack every time the function is called. The Hero1 array will start at that address, and you will be able to change the contents of that array at a later point within the function.
Using char* Hero1, the "Batman" string will not be copied into the stack every time the function is called. The Hero1 variable will be pointing to the original address of the string, hence you will not will be able to change the contents of that string at any point within the function.
When the executable image is generated from your code, the string is placed in the code-section, which is one of several memory-sections within the program. The compiler then replaces the so-called "string assignment" with a so-called "integer assignment".
For example, char* x = "abc" is changed into char* x = (char*)0x82004000 before being compiled into object code, where 0x82004000 is the (constant) address of the string in the program's memory space.
When you do sizeof("abc"), the executable image will not even contain the "abc" string, since there is no "runtime" operation performed on this string.
There is no object code generated for sizeof - the compiler computes this value during compilation, and immediately replaces it with a constant.
You can look into the (intermediate) map file that is usually generated, and see that the input string of that sizeof operation does not appear anywhere.
char Hero1 = "Batman";
should be
char Hero1[] = "Batman";
A couple times you have some issues:
#include <stdio.h>
#include <string.h>
main()
{
char Kid1[12];
// Kid1 can hold an 11-character name
// Kid2 will be 7 characters (Maddie plus null 0)
char Kid2[] = "Maddie";
// Kid3 is also 7 characters, but specifically defined
char Kid3[7] = "Andrew";
// Hero1 will be 7 characters (adding null 0!)
char *Hero1 = "Batman"; //needs to be a pointer
// Hero2 will have extra room just in case
char *Hero2 = "Spiderman"; //needs to be a pointer
char Hero3[25]
Kid1[0] = 'K'; //Kid1 is being defined character-by-character
Kid1[1] = 'a'; //Not efficient, but it does work
Kid1[2] = 't';
Kid1[3] = 'i';
Kid1[4] = 'e';
Kid1[5] = '\0'; // Never forget the null 0 so C knows when the
// string ends
strcpy(Hero3, "The Incredible Hulk");
printf("%s\'s favorite hero is %s.\n", Kid1, Hero1);
printf("%s\'s favorite hero is %s.\n", Kid2, Hero2);
printf("%s\'s favorite hero is %s.\n", Kid3, Hero3);
return 0;
}
You should define all of your vars at the top of the function, its a good C practice.
Other than that, I flagged the issues (and corrected them) with comments.
Solution:
This error you get because String data type is not in C
programming you can print string by using array or char pointer like
1.Array:
#include<stdio.h>
int main(){
char a[]={'a','b','c','d','f','\0'};
printf("%s",a);
return 0;
}
Click here to check the output of solution array
2.char pointer:
#include<stdio.h>
int main(){
char* a="abcd";
printf("%s",a);
return 0;
}
Click here to check the output of solution char pointer

Char x[50] and Char x[100] Output

I'm not used to C as I'm primarily a Java guy, with some knowledge of C++, so forgive me if this is a trivial question. I could not seem to find an answer while searching online.
I'm initializing a char array...
char tcp[50];
So that I can concatenate a const char and a char. In examples I saw an easy way to do this was by creating that empty char array and then using...
strcat(x,y);
To stick them together.
Problem is, printing out the blank char array when it is set to "50" gives me a result of:
X??|?
When I change it to...
char tcp[100];
And print it, its blank. Why is this?
The array contents are undefined, assuming it is a local (automatic) array.
Use:
char tcp[50] = "";
Or:
char tcp[50] = {0};
Or:
char tcp[50];
tcp[0] = 0;
Or:
char tcp[50];
memset(tcp, 0, sizeof(tcp));
As you like.
Always null terminate you char arrays before doing anything:
tcp[0] = '\0';
C happily allocates the space for the array you declare, but it does not set its content to 0.
Therefore, the content of the array you're printing is random (or rather depending in the previous contents of the memory)
When creating an array, the compiler puts it somewhere in memory but does not initialize it, so whatever is in that memory when the program is started will be the initial "string".
Terminate the string manually after you created the array, either by making the whole array "zeroed" out, or just put zero as the first character:
char tcp[50] = { '\0' };
Or
char tcp[50];
/* ... */
tcp[0] = '\0';
The difference here is, you're essentially working with two empty arrays trying to merge them in the memory space of one (not sure if that makes sense for you).
First of all, in C you have to terminate strings with \0. That's something not exposed or visible in Java. Also you essentially used two undefined strings (as there's no value set).
#include <stdio.h>
#include <string.h>
char target[256];
const char source_a[] = "Hello";
const char source_b[] = "World!";
int void(main)
{
target[0] = '\0'; // this essentially empties the string as we set the first entry to be the end. Depending on your language version of C, you might as well write "char target[256] = {'\0'};" above.
strcat(target, source_a); // append the first string/char array
strcat(target, " "); // append a const string literal
strcat(target, source_b); // append the second string
printf("%s\n", target);
return 0;
}
Important: Using strcat() can be unsave, as there's no length check performed, and other than Java, these "strings" have a fixed length (the one you set when defining the variables). If there's no length given, but you copy a string on initialization, that length is taken (e.g. char test[] = "Hello!"; will be 7 chars long (due to terminating \0)).
If you'd like a more Java like approach on strings, use C++ and the std::string class, that performs a lot more similar to Java's strings.

Resources