char string not printed properly in c [duplicate] - c

This question already has answers here:
Strings without a '\0' char?
(6 answers)
Closed 8 years ago.
so i've made a 'guess_the_word' game in c that has a secret word with '-' and'' and whenever you find a correct character this character substitutes '-' or ''.However it seems that the secret word is not printed properly..although the first time it seemed to work.When i used a word with 2 consecutive same characters this problem appeared and then for every word i used.
int sel_size, i;
char select_word[] = "football"; /* the word we are searching*/
sel_size = strlen(select_word);
char secret_word[sel_size];
for (i = 0; i < sel_size; i += 2)
{
secret_word[i] = '_';
}
for (i = 1; i < sel_size; i += 2)
{
secret_word[i] = '-';
}
printf("player 2 the secret word is now %s\n", secret_word);/* it should print "_-_-_-_-" but it prints somthing like this"_-_-_-_-0²#*" */

Strings in C are character arrays terminated by the character '\0'. You never do this to secret_word, so printing it will invoke undefined behavior.
You need to allow for the terminator in the array size:
const size_t sel_size = strlen(select_word);
char secret_word[sel_size + 1]; /* Add 1 to fit the terminator. */
Then after initializing the characters, terminate the string:
secret_word[sel_size] = '\0';
Also, that business of incrementing i by 2 is also wrong, that will exhaust the array too quickly and lead to undefined behavior as you step outside the array. Don't do that. Just do:
memset(secret_word, '_', sel_size);
secret_word[sel_size] = '\0';
UPDATE: Ah, you want underscores separated by dashes. Then you need:
char secret_word[2 * sel_size];
for(size_t i = 0; i < sel_size; ++i)
{
secret_word[2 * i] = '_';
secret_word[2 * i + 1] = '-';
}
secret_word[2 * sel_size - 1] = '\0';
The above can be expressed more succinctly using pointers, but that might be considered more advanced, so indexing it is.

Related

Optimizing a do while loop/for loop in C

I was doing an exercise from LeetCode in which consisted in deleting any adjacent elements from a string, until there are only unique characters adjacent to each other. With some help I could make a code that can solve most testcases, but the string length can be up to 10^5, and in a testcase it exceeds the time limit, so I'm in need in some tips on how can I optimize it.
My code:
char res[100000]; //up to 10^5
char * removeDuplicates(char * s){
//int that verifies if any char from the string can be deleted
int ver = 0;
//do while loop that reiterates to eliminate the duplicates
do {
int lenght = strlen(s);
int j = 0;
ver = 0;
//for loop that if there are duplicates adds one to ver and deletes the duplicate
for (int i = 0; i < lenght ; i++){
if (s[i] == s[i + 1]){
i++;
j--;
ver++;
}
else {
res[j] = s[i];
}
j++;
}
//copying the res string into the s to redo the loop if necessary
strcpy(s,res);
//clar the res string
memset(res, '\0', sizeof res);
} while (ver > 0);
return s;
}
The code can't pass a speed test that has a string that has around the limit (10^5) length, I won't put it here because it's a really big text, but if you want to check it, it is the 104 testcase from the LeetCode Daily Problem
If it was me doing something like that, I would basically do it like a simple naive string copy, but keep track of the last character copied and if the next character to copy is the same as the last then skip it.
Perhaps something like this:
char result[1000]; // Assumes no input string will be longer than this
unsigned source_index; // Index into the source string
unsigned dest_index; // Index into the destination (result) string
// Always copy the first character
result[0] = source_string[0];
// Start with 1 for source index, since we already copies the first character
for (source_index = 1, dest_index = 0; source_string[source_index] != '\0'; ++source_index)
{
if (source_string[source_index] != result[dest_index])
{
// Next character is not equal to last character copied
// That means we can copy this character
result[++dest_index] = source_string[source_index];
}
// Else: Current source character was equal to last copied character
}
// Terminate the destination string
result[dest_index + 1] = '\0';

Right algorithm but wrong implementation?

I am a beginner in C with some experience in python and java. I want to solve a problem with C. The problem goes like this:
Take an input as a sentence with words separated by blank spaces only (assume lower case only), re-write the sentence with the following rules:
1) If a word occurs the first time, keep it the same.
2) If the word occurs twice, replace the second occurrence with the word being copied twice (e.g. two --> twotwo).
3) If the word occurs three times or more, delete all the occurrences after the second one.
Print the output as a sentence. The maximum lengths of the input sentence and each individual word is 500 chars and 50 chars.
Exemplary input: jingle bells jingle bells jingle all the way
Exemplary output: jingle bells jinglejingle bellsbells all the way
The approach I take is:
1) Read the input, separate each word and put them into an array of char pointers.
2) Use nested for loop to go through the array. For each word after the first word:
A - If there is no word before it that is equal to it, nothing happens.
B - If there is already one word before it that is equal to it, change the word as its "doubled form".
C - If there is already a "doubled form" of itself that exists before it, delete the word (set the element to NULL.
3) Print the modified array.
I am fairly confident about the correctness of this approach. However, when I actually write the code:
'''
int main()
{
char input[500];
char *output[500];
// Gets the input
printf("Enter a string: ");
gets(input);
// Gets the first token, put it in the array
char *token = strtok(input, " ");
output[0] = token;
// Keeps getting tokens and filling the array, untill no blank space is found
int i = 1;
while (token != NULL) {
token = strtok(NULL, " ");
output[i] = token;
i++;
}
// Processes the array, starting from the second element
int j, k;
char *doubled;
for (j = 1; j < 500; j++) {
strcpy(doubled, output[j]);
strcat(doubled, doubled); // Create the "doubled form"
for (k = 0; k < j; k++) {
if (strcmp(output[k], output[j]) == 0) { // Situation B
output[j] = doubled;
}
if (strcmp(output[k], doubled) == 0) { // Situation C
output[j] = ' ';
}
}
}
// Convert the array to a string
char *result = output[0]; // Initialize a string with the first element in the array
int l;
char *blank_space = " "; // The blank spaces that need to be addded into the sentence
for (l = 1; l < 500; l++) {
if (output[l] != '\0'){ // If there is a word that exists at the given index, add it
strcat(result, blank_space);
strcat(result, output[l]);
}
else { // If reaches the end of the sentence
break;
}
}
// Prints out the result string
printf("%s", result);
return 0;
}
'''
I did a bunch of tests on each individual block. There are several issues:
1) When processing the array, strcmp, strcat, and strcpy in the loop seem to give Segmentation fault error reports.
2) When printing the array, the words did not show the order that they are supposed to do.
I am now frustrated because it seems that the issues are all coming from some internal structural defects of my code and they are very much related to the memory mechanism of C which I am not really too familiar with. How should I fix this?
One issues jumps out at me. This code is wrong:
char *doubled;
for (j = 1; j < 500; j++) {
strcpy(doubled, output[j]);
strcat(doubled, doubled); // Create the "doubled form"
doubled doesn't point to any actual memory. So trying to copy data to where it points is undefined behavior and will almost certainly cause a SIGSEGV - and it will corrupt memory if it doesn't cause a SIGSEGV.
That needs to be fixed - you can't copy a string with strcpy() or strcat() to an pointer that doesn't point to actual memory.
This would be better, but still not ideal as no checking is done to ensure there's no buffer overflow:
char doubled[ 2000 ];
for (j = 1; j < 500; j++) {
strcpy(doubled, output[j]);
strcat(doubled, doubled); // Create the "doubled form"
This is also a problem with doubled defined like that:
if (strcmp(output[k], output[j]) == 0) { // Situation B
output[j] = doubled;
}
That just points output[j] at doubled. The next loop iteration will overwrite doubled, and the data that output[j] still points to to will get changed.
This would address that problem:
if (strcmp(output[k], output[j]) == 0) { // Situation B
output[j] = strdup( doubled );
}
strdup() is a POSIX function that, unsurprising, duplicates a string. That string will need to be free()'d later, though, as strdup() is the same as:
char *strdup( const char *input )
{
char *duplicate = malloc( 1 + strlen( input ) );
strcpy( duplicate, input );
return( duplicate );
}
As pointed out, strcat(doubled, doubled); is also a problem. One possible solution:
memmove(doubled + strlen( doubled ), doubled, 1 + strlen( doubled ) );
That copies the contents of the doubled string to the memory starting at the original '\0' terminator. Note that since the original '\0' terminator is part of the string, you can't use strcpy( doubled + strlen( doubled ), doubled );. Nor can you use memcpy(), for the same reason.
Your code invokes undefined behavior in several places by writing to memory that is not yet owned, and is likely the cause of the segmentation fault you are seeing. For example, in this segment:
char *result = output[0]; // Initialize a string with the first element in the array
int l;
char *blank_space = " "; // The blank spaces that need to be addded into the sentence
for (l = 1; l < 500; l++) {
if (output[l] != '\0'){ // If there is a word that exists at the given index, add it
strcat(result, blank_space);
strcat(result, output[l]);
By itself...
char *result = output[0]; //creates a pointer, but provides no memory.
...is not sufficient to receive content such as
strcat(result, blank_space);
strcat(result, output[l]);
It needs memory:
char *result = malloc(501);//or more space if needed.
if(result)
{
//now use strcat or strcpy to add content

Printing a reversed string/array in C

I'm trying to print a reversed string/array. I've used the following code and it seems to be able to give my second array, revString, values in the correct order from the first array string. I am also able to print individual characters in both arrays, and I'm able to print the entire string of the first array. However the revString array doesn't print at all. I'm wondering if I am missing a huge point here.
void reverseString(char string[]) {
int i = strlen(string);
int i_2 = 0;
revString arrayet
char revString[i + 1];
char *character;
while (i > -1) {
character = &string[i];
revString[i_2] = *character;
printf("%c", revString[i_2]);
i = i - 1;
i_2 = i_2 + 1;
}
revString[i_2] = '\0';
printf("%d\n", i_2);
printf("%s", revString);
}
The code gives now the following output with example string "Hello World";
dlrow olleH13
As you can see the final printf statement doesn't do anything
In C language indexing is 0 based. so, if you make a string of 10 length, the last character will be at index 9.
In your code, when you are assigning characters to revString, your code is trying to access string[len].
your code should be like this..
int i = strlen(string) - 1;
Your code reverses the string string including the null terminator at string[i]. The resulting array starts with a null terminator, hence printf outputs nothing.
Here is a modified version:
void reverseString(char string[]) {
int i = strlen(string);
int i_2 = 0;
char revString[i + 1];
char character;
while (i > 0) {
i = i - 1;
character = string[i];
revString[i_2] = character;
//printf("%c", revString[i_2]);
i_2 = i_2 + 1;
}
revString[i_2] = '\0';
printf("%d\n", i_2);
printf("%s", revString);
}
Output:
11
dlrow olleH

checking the end of a c string array [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
Im trying to find the end of a word for example school and at the end of a word put a s on to it.
Here is what I have so far:
for (int i = 0; i < 10; i++) {
plural[i] = orig[i];
if (plural[i] == NULL) {
plural[i] = 's';
plural[i + 1] = '\0';
}
}
Your code may function of the string in orig is less than 10 characters long and NULL is defined as 0.
But note that NULL is used to represent the null pointer, not the null byte at the end of a string. NULL can be defined this way:
#define NULL ((void*)0)
In this case, your code would generate a warning upon compilation.
It is considered good style to write '\0' for the null byte at the end of a string. The null character constant has the same value, 0 with type int, but is more explicit for the purpose of representing the null byte.
You should test if orig[i] is non null instead of iterating to 10:
char orig[] = "world";
char plural[10];
int i;
for (i = 0; orig[i] != '\0'; i++) {
plural[i] = orig[i];
}
plural[i] = 's';
plural[i + 1] = '\0';
You can determine the position of null-terminator ('\0') by the following:
int len;
for(len = 0; cstr[len]; ++len);
This is a possible minimal implementation of strlen which is stands for to determine the length of a char array. In example:
#include <stdio.h>
int main() {
char cstr[10] = "Test";
size_t len;
for(len = 0; cstr[len]; ++len);
if(sizeof(cstr) > len + 1) {
cstr[len++] = 's';
cstr[len] = '\0';
}
printf("%s\n", cstr);
}
Note: As David C. Rankin mentioned in comments, you have to protect the array bounds. Knowing that this is an array, you can read its size with the sizeof operator.
The most important part of this exercise is to insure you learn to protect your array bounds. If you declare an array of char [10], then the longest string it can hold is 9-chars + the nul-byte. If you plan to add a character to the end (e.g. 's'), then that means the original string can be no longer than 8-chars.
If you declare plural as:
char plural[10] = ""; /* note the initialization to all `0` */
then the maximum number of characters that can be held in plural in order to use plural as a string is sizeof plural - 1 chars (*preserving space for the nul-byte). So you can set a max for the length of your string with:
char plural[10] = ""; /* note the initialization to all `0` */
int max = sizeof plural - 1;
Then after you find your original string length, you can validate that there is sufficient room for the nul-byte, e.g.
if (len >= max) { /* validate room for 's' available */
fprintf (stderr, "error: adding 's' will exceed array size.\n");
return 1;
}
Putting all the pieces together in a short example, you could do something similar to the following:
#include <stdio.h>
int main (int argc, char **argv) {
char plural[10] = "", *def = "school";
int len = 0,
max = sizeof plural - 1;
if (argc == 1) { /* if no argument given, copy def to plural */
char *p = def;
for (int i = 0; *p && i < max; i++, len++)
plural[i] = *p++;
}
else /* otherwise copy argv[1] to plural */
len = snprintf (plural, max, "%s", argv[1]);
if (len >= max) { /* validate room for 's' available */
fprintf (stderr, "error: adding 's' will exceed array size.\n");
return 1;
}
plural[len] = 's'; /* add 's' - (nul-terminated via initialization) */
printf ("original : %s\nappended : %s\n", argc > 1 ? argv[1] : def, plural);
return 0;
}
Example Use/Output
$ ./bin/plural
original : school
appended : schools
$ ./bin/plural 12345678
original : 12345678
appended : 12345678s
$ ./bin/plural 123456789
error: adding 's' will exceed array size.
note: if you are more comfortable with array indexes than with pointer arithmetic, you can use the following equivalent statement for the length finding and copy:
if (argc == 1) /* if no argument given, copy def to plural */
for (int i = 0; def[i] && i < max; i++, len++)
plural[i] = def[i];
else /* otherwise copy argv[1] to plural */
len = snprintf (plural, max, "%s", argv[1]);
Look things over. There are many, many different ways to approach this. A normal addition would be to include string.h and use strlen and strcpy or memcpy instead of a loop to find your length or copy characters to plural (note: for long strings memcpy will be more efficient, but for 10 char -- it makes no difference) Let me know if you have any questions.

Garbage after string

I've written code to copy a string into another string but with a space between each character. When I run the code there is "garbage" after the string. However, if the for loop at the end is uncommented, there is no garbage after. Anyone know why this is happening?
#include<stdio.h>
#include<string.h>
#define MAX_SIZE 20
main ()
{
char name[MAX_SIZE+ 1];
char cpy[(MAX_SIZE * 2) + 1];
gets(name);
int i = 0;
while (name[i] != '\0' && i < MAX_SIZE)
{
cpy[(i * 2)] = name[i];
cpy[(i * 2) + 1] = ' ';
i++;
}
cpy[strlen(cpy)] = '\0';
printf("%s\n", cpy);
//for (i = 0; i < strlen(cpy); ++i) {
// printf("%c", cpy[i]);
//}
}
The line
cpy[strlen(cpy)] = '\0';
won't work since cpy isn't null terminated so strlen will read beyond the end of name until it either crashes or finds a zero byte of memory. You can fix this by changing that line to
cpy[i*2] = '\0';
If uncommenting the for loop at the end of your function appears to fix things, I can only guess that i gets reset to 0 before your printf call, meaning that printf finds a null terminator on the stack immediately after cpy. If this is what's happening, its very much undefined behaviour so cannot be relied upon.
while (name[i] != '\0' && i < MAX_SIZE)
{
cpy[(i * 2)] = name[i];
cpy[(i * 2) + 1] = ' ';
i++;
}
cpy[(i * 2)] = 0x0;
You have to null terminate the string.
Because you know that you are working with a string, it's a good thing if you initialize your "cpy" array with null character :
char cpy[(MAX_SIZE * 2) + 1] = "\0";
Otherwise, I agreed with simonc answer.
For the sake of completeness:
char* pcpy = cpy;
for (char const* p = fgets(name,sizeof(name)/sizeof(*name),stdin); p && *p; ++p) {
*pcpy++ = *p;
*pcpy++ = ' ';
}
*pcpy = 0;
You should use fgets, not gets in order to prevent your stack to be corrupted by data overruns. Second, You must manually terminate the string stored in the cpy array, since strlen simply counts the number of characters until the very first zero. Hence, if you haven't already terminated cpy the result of strlen(cpy) will be undefined and most likely crash your program.

Resources