Why string termination not happening at null character - c

Why it prints null character in second string?
Declaring character array should automatically add null character at end. Is this dependent on compiler?
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
bool ChckStrng(char *str);
void main()
{
char a[] = "hello";
char b[] = "abc";
printf("a:%d\n", ChckStrng(a));
printf("b:%d\n", ChckStrng(b));
}
bool ChckStrng(char *str)
{
int count[26];
while(str != NULL)
{
printf(":%d:\n", *str - 'a');
if(++count[*str - 'a'] > 1)
return false;
str = str + 1;
}
printf("end\n");
return true;
}
Output1:
:7:
:4:
:11:
:11:
a:0
:0:
:1:
:2:
:-97:
:-97:
b:0

You are comparing pointers here, in other words, if str is set to NULL:
while(str != NULL) /* str holds the address of a char */
You need to compare characters to the null-terminator, in other words, check whether the character str points to is the null-terminator:
while(*str != '\0')

str is a pointer which holds the address *str holds the value
bool ChckStrng(char *str)
{
int count[26];
while(*str != '\0')
{
printf(":%d:\n", *str - 'a');
if(++count[*str - 'a'] > 1)
return false;
str = str + 1;
}
printf("end\n");
return true;
}

You are comparing the value of the pointer itself instead of the value pointer points to.
while( *str != '\0' )
{
Not the difference: *str, and \0 which is a null character.
NULL is a null pointer not a null character.

Pointers are different than characters. In 'C' null termination of character array is signified by ascii value 0 or NUL

Related

Capitalize first letter of a word in a string in C

I have to capitalize the first letter of every word (words are separated by a space) into a given array of char. I wrote the code but I can't figure out why it's not working nor displaying anything in output.
Here's the code:
void LetterCapitalize(char *str) {
char *str2;
int i = 0;
str2[i] = toupper(str[0]);
i++;
while (str[i]) {
if (str[i] == ' ') {
str2[i] = str[i];
str2[i + 1] = toupper(str[i] + 1);
i += 2;
} else {
str2[i] = str[i];
i++;
}
}
printf ("%s", str2);
}
And here's the main:
int main(void) {
char stringa[16] = "some string here";
LetterCapitalize(stringa);
return 0;
}
There are multiple problems:
The string defined in main is not null terminated because the initializer has exactly 16 characters, the defined length of the array, so there is no space for the null terminator. It is safer to omit the array length and let the compiler compute it from the initializer, including the null terminator:
char stringa[] = "some string here"; // sizeof stringa == 17
str2 is uninitialized: storing characters to it has undefined behavior. You could instead either modify the argument string in place or allocate a copy and modify that.
the logic in LetterCapitalize is risky: you assume that words are separated by a single space and that the string does not end with a space.
the char argument to toupper() should be cast as (unsigned char) to avoid undefined behavior on negative char values on platforms where the type char is signed by default.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
char *LetterCapitalize(char *str) {
unsigned char c, last = ' ';
// uppercase characters that follow a space or at the start of the string
for (size_t i = 0; (c = str[i]) != '\0'; last = c, i++) {
if (last == ' ')
str[i] = toupper(c);
}
return str;
}
int main() {
char stringa[] = "some string here";
puts(LetterCapitalize(stringa));
return 0;
}
For starters this array
char stringa[16] = "some string here";
does not contain a string because it does not have a space to accommodate the terminating zero character '\0' of the string literal used as an initializer.
It would be better to declare it the following way without explicit specifying its size
char stringa[] = "some string here";
So this while loop
while (str[i])
can invoke undefined behavior.
Also you are using the uninitialized pointer str2.
char *str2;
//...
str2[i] = toupper (str[0]);
that again invokes undefined behavior.
Pay attention to that a passed string can contain more than one space between words and moreover can contain leading and trailing spaces. So this if statement also can invoke undefined behavior due to skipping the terminating zero character '\0' of the source string
if (str[i] == ' ')
{
str2[i] = str[i];
str2[i + 1] = toupper (str[i] + 1);
i += 2;
}
Hence your approach is in general wrong.
Such a function should return the modified source string.
Instead of the for loop it is better to use standard C functions strspn and strcspn.
Here is a demonstration program.
#include <string.h>
#include <stdio.h>
#include <ctype.h>
char * LetterCapitalize( char *s )
{
const char *delim = " \t";
for (char *p = s; *p; p += strcspn( p, delim ) )
{
p += strspn( p, delim );
if (*p) *p = toupper( ( unsigned char )*p );
}
return s;
}
int main( void )
{
char stringa[] = "some string here";
puts( stringa );
puts( LetterCapitalize( stringa ) );
}
The program output is
some string here
Some String Here

Error with manually copying string using pointers

I am creating this program as part of an assignment for college. The objective is to copy char* slogan = "Comp10120 is my favourite module"; to a new string while removing consonants and capitalising all letters. This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
void printStrings();
char *slogan = "Comp10120 is my favourite module";
char *p = slogan;
char *slogan_copy = NULL;
int main ()
{
//Get size of original string
int slogan_size = 0;
while (*p++ != '\0')
slogan_size++;
// Dynamically allocate memory to copy of string
slogan_copy = (char*) malloc ((slogan_size+1) * sizeof(char));
//Place string terminator at end of copy string
slogan_copy[slogan_size] = '\0';
//Reset p pointer to start of string
p = slogan;
int offset = 0;
while (*p != '\0')
{
//If the current element in the string is a consonant,
//or as defined in the if statement,
//if p is not a vowel and is between a and z or A and Z:
if ((!(*p == 'a' || *p == 'e' || *p == 'i' || *p == 'o' || *p == 'u'))
&& (((*p > 'a') && (*p < 'z')) || ((*p > 'A') && (*p < 'Z'))))
p++;
else
//Copy element to slogan_copy and capitalise
slogan_copy[offset++] = *p++;
slogan_copy[offset] = toupper(slogan_copy[offset]);
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printStrings();
return 0;
}
void printStrings ()
{
printf("Origianl String: %s\n",*slogan);
printf("Modified String: %s",*slogan_copy);
}
When I try to execute, I get the error
initializer element is not constant
char *p = slogan;
^~~~~~
I am assuming that it is because I am trying to perform operations on slogan as if it was just a regular array of characters, and not a pointer to a string. However, I don't know how to fix this error.
In addition to this, I tried changing char*slogan = "Comp10120 is my favourite module"; to char slogan[] = "Comp10120 is my favourite module"; to see if it would work, out of curiosity. It complies, but crashes upon execution. Any ideas as to how I could modify my code for it to work?
you have quite a lot of mistakes in your program. do consider your use of global variables and consider using const where it is needed, However it is a preety good starting point so I have tested your program and it seems to work with 4 simple corrections:
1.remove p initialazation in the global env
8: //char *p = slogan;
9: char *p;
set p within main block
int main ()
{
p = slogan;
...
}
remove the astrix from the slogan in your printf statments it is already a pointer to a char array
printf("Origianl String: %s\n",slogan);
printf("Modified String: %s",slogan_copy);
hope this helps
A few improvements are needed.
1) In the function printf format %s expects pointer to the buffer. There is no need to dereference slogan or slogan_copy.
2)
slogan_copy[offset++] = *p++;
slogan_copy[offset] = toupper(slogan_copy[offset]);
The above will not work. It will make the next character upper not the current.
3) In C language there is no need to cast malloc.
4) Global variables should be avoided at all cost. The break encapsulation.
Pass variables as a parameters, you will get greater flexibility.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
void printStrings (const char *format, const char *s);
int main (void)
{
char *slogan = "Comp10120 is my favourite module";
char *p = slogan;
char *slogan_copy;
int offset = 0;
int slogan_size = 0;
//Get size of original string
while (*p++ != '\0')
slogan_size++;
// Dynamically allocate memory to copy of string
slogan_copy = malloc ((slogan_size+1) * sizeof(char));
//Place string terminator at end of copy string
slogan_copy[slogan_size] = '\0';
//Reset p pointer to start of string
p = slogan;
while (*p != '\0')
{
//If the current element in the string is a consonant,
//or as defined in the if statement,
//if p is not a vowel and is between a and z or A and Z:
if ((!(*p == 'a' || *p == 'e' || *p == 'i' || *p == 'o' || *p == 'u'))
&& (((*p > 'a') && (*p < 'z')) || ((*p > 'A') && (*p < 'Z'))))
p++;
else{
//Copy element to slogan_copy and capitalise
slogan_copy[offset] = *p;
slogan_copy[offset] = toupper(slogan_copy[offset]);
*p++;
offset++;
}
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printStrings("Origianl String: %s\n", slogan);
printStrings("Modified String: %s\n", slogan_copy);
return 0;
}
void printStrings (const char *format, const char *s)
{
printf(format,s);
}
Output:
Origianl String: Comp10120 is my favourite module
Modified String: O10120 I AOUIE OUE
As per your request in a comment here is a simplified and correct version:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main()
{
const char *slogan = "Comp10120 is my favourite module";
// Dynamically allocate memory to copy of string
char *slogan_copy = malloc((strlen(slogan) + 1) * sizeof(char));
//Reset p pointer to start of string
const char *p = slogan;
int offset = 0;
while (*p != '\0')
{
//If the current element in the string is a consonant,
//or as defined in the if statement,
//if p is not a vowel and is between a and z or A and Z:
char c = toupper(*p++);
if (!isalpha(c) || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' || )
slogan_copy[offset++] = c;
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printf("Origianl String: %s\n", slogan);
printf("Modified String: %s\n", slogan_copy);
return 0;
}
Output:
Origianl String: Comp10120 is my favourite module
Modified String: O10120 I AOUIE OUE
Your code is not indented correctly: the else branch has 2 statements but they are not wrapped inside a block with { and }, so only the first statement in executed conditionally and the second is always executed, causing unexpected behavior regarding the uppercasing feature.
Furthermore, the uppercasing is not applied to the correct offset as offset is incremented too early, and uppercase vowels would be removed too.
A sane rule for coding style is to always use braces for all compound statements but the simplest ones. Rewrite the test this way:
int c = toupper((unsigned char)*p++);
if (!isalpha(c) || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
//Copy and capitalise element to slogan_copy
slogan_copy[offset++] = c;
}
There are other problems in the code, eg: passing incorrect data for the printf arguments, and using global variables for no good reason.
Here is an improved version:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main() {
const char *slogan = "Comp10120 is my favourite module";
char *slogan_copy = NULL;
//Get size of original string
int slogan_size = 0;
while (slogan[slogan_size] != '\0')
slogan_size++;
// Dynamically allocate memory to copy of string
slogan_copy = malloc(slogan_size + 1);
//Reset p pointer to start of string
const char *p = slogan;
int offset = 0;
while (*p != '\0') {
int c = toupper((unsigned char)*p++);
if (!isalpha(c) || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
//Copy and capitalise element to slogan_copy
slogan_copy[offset++] = c;
}
}
//Place string terminator after last element copied.
slogan_copy[offset] = '\0';
printf("Original string: %s\n", slogan);
printf("Modified string: %s\n", slogan_copy);
return 0;
}

How to write your own strchr in c using pointers?

Ok, so I have an assignment here from my professor. Here it is:
Write a function called strchr406. It is passed 2 parameters: a string and a char Here is the prototype for the function:
char *strchr406(char str[], char ch);
The function should return a pointer to the first instance of ch in str. For example:
char s[ ] = "abcbc";
strchr406(s, 'b'); // returns s + 1 (i.e., a pointer to the first 'b' in s)
strchr406(s, 'c'); // returns s + 2
strchr406(s, 'd'); // returns 0
He is asking us to write our own version of strchr using pointers. I looked up online for resources but none of it matches what he is asking us to do. I'm working with a group of other students, and none of us could figure this out.
How do we RETURN "s + 1"?
So far, I have this:
(I also put it online if that's easier: https://repl.it/FVK8)
#include <stdio.h>
#include "string_problems.h"
int main() {
char s[ ] = "abcbc";
strchr406(s, 'b'); // returns s + 1 (i.e., a pointer to the first 'b' in s)
strchr406(s, 'c'); // returns s + 2
strchr406(s, 'd'); // returns 0
printf("this should return %s\n", strchr406(s, 'c'));
return 0;
}
char *strchr406(char str[], char ch) {
char *p = str;
int index = 0;
while (*str != ch) {
++str;
++index;
}
if (*str == ch) {
return p + index;
} else {
return 0;
}
}
I'm getting weird outputs. Any help is appreciated.
From the manual:
char *strchr(const char *s, int c); --> the 2nd argument is an int
The strchr() and strrchr() functions return a pointer to the matched character or NULL if the character is not found.
The terminating null byte is considered part of the string,
so that if c is specified as '\0', these functions return a pointer to the terminator.
[there is no defined behaviour if the first argument happens to be NULL]
char *strchr42(char *str, int ch)
{
for (;; str++) {
if (*str == ch) return str;
if (!*str) return NULL;
}
return NULL;
}
or even shorter:
char *strchr42a(char *str, int ch)
{
do {
if (*str == ch) return str;
} while (*str++) ;
return NULL;
}
Here you are
#include <stdio.h>
char * strchr406( const char str[], char ch )
{
while ( *str && *str != ch ) ++str;
return ( char * )( ch == *str ? str : NULL );
}
int main(void)
{
char s[ ] = "abcbc";
printf( "strchr406(s, 'b') == s + 1 is %d\n", strchr406(s, 'b') == s + 1 );
printf( "strchr406(s, 'c') == s + 2 is %d\n", strchr406(s, 'c') == s + 2 );
printf( "strchr406(s, 'd') == 0 is %d\n", strchr406(s, 'd') == 0 );
printf( "this should return %s\n", strchr406(s, 'c'));
return 0;
}
The program output is
strchr406(s, 'b') == s + 1 is 1
strchr406(s, 'c') == s + 2 is 1
strchr406(s, 'd') == 0 is 1
this should return cbc
Say your professor that it will be correct to declare the function like
char * strchr406( const char str[], char ch );
^^^^^
Moreover the standard function has the following declaration
char *strchr(const char *s, int c);
because character literals in C have the type int.
So you could write the function even the following way
char * strchr406( const char str[], int ch )
{
unsigned char c = ch;
while ( *str && ( unsigned char )*str != c ) ++str;
return ( char * )( c == ( unsigned char )*str ? str : NULL );
}
As for your function then there is no sense to use the variable index because the pointer str is increased itself.
There are a couple of small things you should add or re-organize in your code to make it work.
First, this piece of code
while (*str != ch) {
++str;
++index;
}
will not stop at the end of your string and will continue looping until it finds your char somewhere after the string in the virtual memory of the process.
So you should probably have a condition for stopping that checks for the end of the string (strings in C ends with the char \0, ASCII code = 0):
while (*str != '\0')
Second thing, you are comparing ch with the current char of the string after the loop. You should probably move this code inside the loop: you need to check every character of the string against ch. Also, you don't need to use both an index and increment the pointer str. So you can get rid of the index variable. If at anytime in your loop you find the correct ch then you can directly return the pointer to it using str. If you get out of your loop, it means that you did not find the ch inside str and then you can return NULL (cf man strchr for more on return values).
char *strchr406(char str[], char ch)
{
while (*str != '\0')
{
if (*str == ch)
{
return (str);
}
str++;
}
return (NULL);
}
Note that in this context, even though NULL is 0, it's better to use NULL since you are supposed to return a pointer.
Last thing, if you want to do exactly like strchr, then if ch is '\0' you should return the pointer to the '\0' at the end of the string str. From the man: The terminating null byte is considered part of the string, so that if c is specified as '\0', these functions return a pointer to the terminator. So your code becomes:
char *strchr406(char str[], char ch)
{
while (*str != '\0')
{
if (*str == ch)
{
return (str);
}
str++;
}
/**
* if ch is '\0', you should return
* the pointer to the `\0` of the string str
*/
if (*str == ch)
{
return (str);
}
return (NULL);
}
Note: Thanks to #chux for pointing this last thing out.
Note 2: You don't need to check if str is NULL in this context.
Note 3: The "official" prototype for strchr is char *strchr(const char *s, int c); so depending on your project requirements you might want to update your prototype to match to this one.

passing pointer to strcat does not update the string

I'm writing my own version of strcat in C following K&R. This is my code:
#define MAXVALUE 1000
void concat(char *s, char *t)
{
while (*s++)
;
while (*s++ = *t++)
;
}
/* test */
int main()
{
char s1[MAXVALUE];
char s2[] = "Jim!";
s1[0] = 'H', s1[1] = 'i', s1[2] = ' ';
s1[3] = '\0';
concat(s1, s2);
printf("%s\n", s1);
return 0;
}
The idea is to copy s2 into s1 to obtain "Hi Jim!". I made sure that s1 is large enough to contain both strings. However, when I run this, it outputs "Hi", so basically it does not update the string s1. I'm at a loss as to why: s1 still points at s1[0] = 'H' and after running concat the '\0' in s1[3] should have been replaced and s1 should be terminated with '\0' at s1[7].
P.S. Note that as in K&R, my version of strcat does not match the standard library one. In particular, the return value is void.
In your code, the problem is, after reaching the terminating NUL, you're advancing the pointer *s++, so, it is included in the destination string, making the printf() to interpret as the end of string. As per the design rule of string concatination, you need to remove [or replace, or overwrite] the terminating NUL and add the second string.
To avoid the terminating NUL apperaing in the output string, do not increment the pointer when it reaches NUL, instead, start copying the next string from that particular location itself.
Check the below code.
#include <stdio.h>
#include <stdlib.h>
#define MAXVALUE 1000
void concat(char *s, char *t)
{
while (*s) s++; //after NUL, do not increment, move on to copying
while (*s++ = *t++)
;
}
/* test */
int main()
{
char s1[MAXVALUE];
char s2[] = "Jim!";
s1[0] = 'H', s1[1] = 'i', s1[2] = ' ';
s1[3] = '\0';
concat(s1, s2);
printf("%s\n", s1);
return 0;
}
Check the below code:
void concat(char *s, char *t)
{
while (*s != '\0')
s++;
while (*s++ = *t++)
;
}
/* test */
int main()
{
char s1[MAXVALUE];
char s2[] = "Jim!";
s1[0] = 'H', s1[1] = 'i', s1[2] = ' ';
s1[3] = '\0';
concat(s1, s2);
printf("%s\n", s1);
return 0;
}
Thanks to #MOehm, I fixed the code by simply inserting s-- to compensate for going past the null terminator:
void concat(char *s, char *t)
{
while (*s++)
;
s--;
while (*s++ = *t++)
;
}

Function To Match The Last Character Of A String

So this is a problem : Write the function strend(s,t), which returns 1 if the char t occurs at the end of the string s, and zero otherwise.
This is my code:
int strend(char*, char);
int main()
{
int n = -1;
char str1[6] = "Hello", char1;
printf("Enter a character: ");
char1 = getchar();
n = strend(str1, char1);
printf("n = %d", n);
return 0;
}
int strend(char* str1, char str2)
{
while(*str1 != '\0')
{
str1++;
}
if(*str1 == str2)
{
return 1;
}
else
{
return 0;
}
}
However the character matching does not perform as intended. Where the mistake?
Thanks.
You're comparing the character to the \0 string terminator.
int strend(char* str1, char str2)
{
if (*str1 == '\0') {
return 0;
}
while(*str1 != '\0') /* removed ; that shouldn't be there */
{
str1++;
}
/* at this point, str1 is pointing to the 0-terminator */
str1--; /* pointer now points to last character of the string, not 0-terminator */
if(*str1 == str2)
{
return 1;
}
else
{
return 0;
}
}
I'll try my own explanation.
Suppose your while() loop has reached the last non-zero character of your string str1.
In this case, the line while( *str1 != '\0' ) is "asking" if this character is zero or not.
Since it is the character that you are looking for, logically it cannot be '\0'.
Then the comparison expressión is "true", and the increment str1++; is performed.
Now *str1 is the character '\0', and the immediate next iteration gives "false" when evaluating *str1 != '\0'.
Then the while() block is finished, and the program continues in the line if(*str1 == str2).
Here, the value *str1, which is '\0', is compared against str2, giving always the result "false".
However, the desired character is still in the immediate previous memory position of str1.
So, you can decrement str1 and then comparing, or well you can compare str2 against (str1 - 1).
// Option 1
str1--;
if(*str1 == str2)
//Option 2
if ((str1 - 1) == str2)
Without any error checking, (you can do that) here is a one liner that will check that the last character is matched:
int strend(char* str1, char str2)
{
return ((str1[strlen(str1)-1]) == str2)?(1):(0);
}
Or in a more readable form:
int strend(char* str1, char str2)
{
return ((str1[strlen(str1)-1]) == str2);
}

Resources