I am a bit confused on why my code is not iterating a string in C programming.
Essentially.
I have this function here
int atoi(const char *s[]){
printf(" The length is %d",strlen(s));
int length = strlen(s);
for(int i = 0; i< length; ++i){
printf("This is %s",*(s+i));
}
return 0;
}
My first question
When doing strlen(s) why does it work, but when doing strlen(*s) it does not work, isn't strlen supposed to be taking in the value not the pointer so the latter should work?
How would I go about looping through the pointer that points to the string in memory?
Cheers
Do not use standard function names
strlen takes a pointer to char and returns size_t (not int)
Your print format is wrong.
Your function takes an array of pointers not a pointer to char
int myfunc(const char *s)
{
size_t length = strlen(s);
printf(" The length is %zu", length);
for(size_t i = 0; i< length; ++i){
printf("This is '%c'\n",*(s+i));
}
return 0;
}
For starters you should not name your user-defined functions the same ways as standard C functions.
There is already standard C function with the name atoi declared in header <stdlib.h>. Using the same name can result in compiler or linker errors.
Your function atoi declared like
int atoi(const char *s[]){
that is the same as
int atoi(const char **s){
due to adjusting by the compiler a parameter having an array type to pointer to the array element type accepts a "double" pointer. So using the function strlen with the double pointer invokes undefined behavior
printf(" The length is %d",strlen(s));
The function strlen expects an argument of the type char * or const char * but not an argument of the type const char **.
Moreover the return value of the function strlen has the type size_t. And using the conversion specifier %d in a call of printf with a corresponding argument expression of the type size_t again invokes undefined behavior. You have to use the conversion specifier %zu instead of %d.
This for loop
for(int i = 0; i< length; ++i){
printf("This is %s",*(s+i));
}
does not make sense because the variable length actually does not contain the number of elements in the passed array.
You need either explicitly to pass the number of elements in the array or the array should have a sentinel value.
And the return type of the function int also does not make sense because the returned value 0 reports nothing useful to the caller of the function.
If the function is designed only to traverse a string in a for loop then it should be declared like for example
char * func( const char *s )
{
for ( const char *p = s; *p != '\0'; ++p )
{
putchar( *p );
}
return ( char * )s;
}
If you want to pass to the function an array of strings then the function can be implemented for example the following way as shown in the demonstration program below provided that the array contains a sentinel value
#include <stdio.h>
void func( const char **s )
{
for ( ; *s != NULL; ++s )
{
printf( "%s ", *s );
}
}
int main( void )
{
const char * s[] = { "Hello", "World!", NULL );
func( s );
putchar( '\n' );
}
The program output is
Hello World!
Question:
when doing strlen(s) why does it work, but when doing strlen(*s) it
does not work?
Answer:
strlen() expects an argument of type const char *, not a const char **. You passed an array of pointers to char to the function, which decayed to a char **. From the man page:
The strlen() function calculates the length of the string pointed
to by s, excluding the terminating null byte ('\0').
Question:
How would I go about looping through the pointer that points to the
string in memory?
printf("This is %s",*(s+i));
Answer:
Assuming you wanted to pass a string (an array of null-terminated bytes) to the function and print it char by char, change the function declaration to take a const char *, and use the correct format specifier in the call to printf() (The %s format specifier expects a pointer to char, not a char itself). Change the call to:
printf ("%c", *(s + i));
Note that *(s + i) is equivalent to s[i], which is more readable. So the above call to printf() can be rewritten as:
printf ("%c", s[i]);
Aside: strlen() returns size_t, not an int. Do not mismatch numeric types.
Related
I have problems understanding how char* works.
In the example below, the struses is called by main().
I created a buf to store the const variable because I want to make a modifiable copy of s1, then I just call the sortString().
This version makes sense to me as I understand that char[] can be modified:
#include "common.h"
#include <stdbool.h>
void sortString(char string[50]);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1); // <===== input = "perpetuity";
sortString(buf);
printf("%s\n", buf); // prints "eeipprttuy"
return true;
}
void sortString(char string[50])
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
However, in this version I deliberately changed the type to char* which is supposed to be read-only. Why do I still get the same result?
#include "common.h"
#include <stdbool.h>
void sortString(char *string);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1);
sortString(buf);
printf("%s\n", buf);
return true;
}
void sortString(char *string) // <==== changed the type
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
This is why I think char * is read only. I get a bus error after trying to to modify read[0]:
char * read = "Hello";
read[0]='B';// <=== Bus error
printf("%s\n", read);
The compiler adjusts the type of the parameter having an array type of this function declaration
void sortString(char string[50]);
to pointer to the element type
void sortString(char *string);
So for example these function declarations are equivalent and declare the same one function
void sortString(char string[100]);
void sortString(char string[50]);
void sortString(char string[]);
void sortString(char *string);
Within this function
void sortString(char *string)
there is used the character array buf that stores the copy of the passed array (or of the passed string literal through a pointer to it)
char buf[50];
strcpy(buf, s1);
sortString(buf);
So there is no problem. s1 can be a pointer to a string literal. But the content of the string literal is copied in the character array buf that is being changed
As for this code snippet
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== still prints "Hello"
then it has undefined behavior because you may not change a string literal.
From the C Standard (6.4.5 String literals)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
Pay attention to that in C++ opposite to C string literals have types of constant character arrays. It is advisable also in C to declare pointers to string literals with the qualifier const to avoid undefined behavior as for example
const char * read = "Hello";
By the way the function sortString has redundant swappings of elements in the passed string. It is better to declare and define it the following way
// Selection sort
char * sortString( char *s )
{
for ( size_t i = 0, n = strlen( s ); i != n; i++ )
{
size_t min_i = i;
for ( size_t j = i + 1; j != n; j++ )
{
if ( s[j] < s[min_i] )
{
min_i = j;
}
}
if ( i != min_i )
{
char c = s[i];
s[i] = s[min_i];
s[min_i] = c;
}
}
return s;
}
char * does not mean read-only. char * simply means pointer to char.
You have likely been taught that string literals, such as "Hello", may not be modified. That is not quite true; a correct statement is that the C standard does not define what happens when you attempt to modify a string literal, and C implementations commonly place string literals in read-only memory.
We can define objects with the const qualifier to say we intend not to modify them and to allow the compiler to place them in read-only memory (although it is not obligated to). If we were defining the C language from scratch, we would specify that string literals are const-qualified, the pointers that come from string literals would be const char *.
However, when C was first developed, there was no const, and string literals produced pointers that were just char *. The const qualifier came later, and it is too late the change string literals to be const-qualified because of all the old code using char *.
Because of this, it is possible that a char * points to characters in a string literal that should not be modified (because the behavior is not defined). But char * in general does not mean read-only.
Your premise that the area pointed by a char* isn't modifiable is false. This is perfectly line:
char s[] = "abc"; // Same as: char s[4] = { 'a', 'b', 'c', 0 };
char *p = s; // Same as: char *p = &(s[0]);
*p = 'A';
printf("%s\n", p); // Abc
Demo
The reason you had a fault is because you tried to modify the string created by a string literal. This is undefined behaviour:
char *p = "abc";
*p = 'A'; // Undefined behaviour
printf("%s\n", p);
One would normally use a const char * for such strings.
const char *p = "abc";
*p = 'A'; // Compilation error.
printf("%s\n", p);
Demo
Regarding
char * read = "Hello";
read[0]='B';
printf("%s\n", read); // still prints "Hello"
you have tripped over a backward compatibility wart in the C specification.
String constants are read-only. char *, however, is a pointer to modifiable data.
The type of a string constant ought to be const char [N] where N is the number of chars given by the contents of the constant, plus one. However, const did not exist in the original C language (prior to C89). So there was, in 1989, a whole lot of code that used char * to point to string constants. So the C committee made the type of string constants be char [N], even though they are read-only, to keep that code working.
Writing through a char * that points to a string constant triggers undefined behavior; anything can happen. I would have expected a crash, but the write getting discarded is not terribly surprising either.
In C++ the type of string constants is in fact const char [N] and the above fragment would have failed to compile. Some C compilers have an optional mode you can turn on that changes the type of string constants to const char [N]; for instance, GCC and clang have the -Wwrite-strings command line option. Using this mode for new programs is a good idea.
Yout long examples can be reduced to your last question.
This is why I think char * is read only, get bus error after attempt
to modify read[0]
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== Bus error
"Hello" is a string literal . Attempt to modify the string literal manifested itself by the Bus Error.
Your pointer is referencing the memory which should not be modified.
How to sort it out? You need to define pointer referencing the modifiable object
char * read = (char []){"Hello"};
read[0]='B';
printf("%s\n", read);
So as you see declaring it as modifiable is not making it modifiable.
I am trying to write a C function that compares tho strings not as pointer equality but content equality. But I get an error
error: invalid type argument of unary ‘*’ (have ‘int’)
This is my code:
#include <stdio.h>
#include <stdlib.h>
int stringcompare(char * str1, char * str2, int strSize){
char * word1 = str1;
char * word2 = str2;
for (int i = 0; i < strSize; i++) {
if (*word1[i] != *word2[i]) {
printf("The strings are DIFFERENT!");
return 1;
}
}
printf("The strings are a MATCH!");
return 0;
}
int main(void){
char * str1 = "Hello World!";
char * str2 = "Hello World!";
stringcompare(str1, str2, 13);
}
For an array, pointed to by *ptr, an element at position i is dereferenced by *(ptr + i), which is the equivalent of ptr[i] and not *ptr[i].
This if statement
if (*word1[i] != *word2[i]) {
is incorrect because the expressions word1[i] and word2[i] have the type char. So you may not apply the dereference operator for an object of the type char.
You should write for example
if ( word1[i] != word2[i]) {
Pay attention to that the standard string function strcmp has only two parameters and it returns either negative value, or zero or a positive value depending on whether the first string is greater than the second string or is equal to the second string or is less than the second string.
It seems you mean another standard string function strncmp that indeed has three parameters..
Also you need to check whether the zero terminating character was already encountered.
Apart from this the function parameters should have the qualifier const because the passed strings are not being changed within the function.
The function can be declared and defined the following way
int stringcompare( const char *s1, const char *s2, size_t n )
{
while ( n && *s1 && *s1 == *s2 )
{
++s1;
++s2;
--n;
}
return n == 0 ? 0 : *s1 - *s2;
}
I have made a strcpy() function
in C, and I am copying words from one array to other not just letters, but when I run it I am getting Segmentation fault what to do?
#include <stdio.h>
void strcpy1(char *dest[], char source[])
{
while ((*dest++ = *source++));
}
int main()
{
char source[3][20] = { "I", "made", "this" };
char dest[3][20];
strcpy1(&dest, source);
//printing destination array contents
for (int i = 0; i < 3; i++) {
printf("%s\n", dest[i][20]);
}
return 0;
}
There are multiple problems in your code:
the prototype for your custom strcpy1 function should be:
void strcpy1(char *dest[], char *source[]);
the arrays source and dest are 2D char arrays: a very different type from what strcpy1 expects, which are arrays of pointers. Change the definition to:
char *source[4] = { "I", "made", "this" };
char *dest[4];
you should pass the destination array as dest instead of &dest
the source array should have a NULL pointer terminator: it should be defined with a length of at least 4. Same for the destination array.
in the print loop dest[i][20] refers to a character beyond the end of the i-th string. You should just pass the string as dest[i].
Here is a modified version:
#include <stdio.h>
void strcpy1(char *dest[], char *source[])
{
while ((*dest++ = *source++));
}
int main()
{
char *source[4] = { "I", "made", "this" };
char *dest[4];
strcpy1(dest, source);
//printing destination array contents
for (int i = 0; dest[i]; i++) {
printf("%s\n", dest[i]);
}
return 0;
}
Note that it is somewhat confusing to name strcpy1 a function that has very different semantics from the standard function strcpy().
The %s specifier is for strings, eg, a char* referring to the first character of a string.
When you pass dest[i][20] to the printf function, it is not a char*. It is a single char, the 21st char (valid indices are 0-19, for a total of 20 elements).
So it is an array-out-of-bounds-index, and also, not a char* as printf expects.
printf("%s\n", dest[i][20]);
The main problem is: as soon as I send a string from one function to another, this second function doesn't really get the string as a parameter.
In detailled:
I have a function void myfunc() contains a word. This word should be send to another function, so it can count the length of it. That's what I've written so far:
void myfunc(int (*countlength)(char ch)){
char word[10] = "Hello\n";
int result = countlength(&word);
printf("Length of word: %d\n", result);
}
The word is being send to this function countlength(char* word):
int countlength(char* word) {
int length = strlen(word);
return length;
}
However the function countlength() can't count it's length and I don't know why...
The thing is, it works when the word is in the main function. Does anybody know why my Code doesn't work?
Two mistakes:
void myfunc(int (*countlength)(char ch)){
should be
void myfunc(int (*countlength)(char* ch)){
instead, as the function accepts char pointers.
Secondly,
int result = countlength(&word);
should be
int result = countlength(word);
as word is already a char*.
What you're passing to the function doesn't match what it's expecting.
&word has type char (*)[10], i.e. a pointer to an array of size 10. The function expects a char *, so just pass word. Arrays are converted to a pointer to their first element when passed to a function, so the types will match.
This parameter declaration of a function pointer
int (*countlength)(char ch)
does not correspond to the function declaration used as an argument for this parameter
int countlength(char* word)
So you need to declare the parameter like
int (*countlength)(char *ch)
In fact the identifier ch is redundant. You could just write
int (*countlength)(char *)
That is the declaration of the function myfunc will look like
void myfunc(int (*countlength)(char *));
You declared a character array within the function like
char word[10] = "Hello\n";
So the expression used as an argument in this call
countlength(&word)
has the type char ( * )[10] instead of the expected type char *.
There is no need to use the address of operator. The array designator used as an argument in this call
countlength( word )
is implicitly converted to a pointer to the first element of the array and has the type char *.
This function
int countlength(char* word) {
int length = strlen(word);
return length;
}
does not change its argument. So it should be declared at least like
int countlength( const char* word) {
int length = strlen(word);
return length;
}
The used standard C string function strlen has the return type size_t. In general an object of the type int can be not enough large to store possible lengths of strings.
So the function should be declared like
size_t countlength( const char* word) {
return strlen(word);
}
Thus returning to the function myfunc it should look like
void myfunc( size_t ( *countlength )( const char * ) )
{
char word[10] = "Hello\n";
size_t result = countlength( word );
printf( "Length of word: %zu\n", result );
}
Well; if you use the code like this, it's working just fine
When you declare an array, its name has a type of a pointer so here word has a type of char* and it's the pointer of the array's first element
#include <stdio.h>
#include <string.h>
int countlength(char* word) {
int length = strlen(word);
return length;
}
void myfunc(){
char word[10] = "Hello\n";
int result = countlength(word);
printf("Length of word: %d\n", result);
}
main(){
myfunc();
}
Length of word: 6
I don't fully understand how to work with pointers.
Inside the function is where I need to write code to return the length of input string.
int mystrlen (const char *s)
{
char *s[1000], i;
for(i = 0; s[i] != '\0'; ++i);
printf("Length of string: %d, i");
return 0;
}
Could you tell me how to make it work?
Thank you!!
Remove char *s[1000], declare int i instead of char i, and return i rather than 0:
You need to remove the s inside the function body because it is "shadowing the variable" s that is a function parameter, meaning the s function parameter cannot be read at all.
Changing char i to int i will likely increase the range of possible values to return. If you pass a string with 128 characters in it, char i would result in returning -128 if it is a signed 8-bit type. int is guaranteed to be 16-bit, allowing for strings up to 32767 characters (more than enough for most common uses of a string length function).
You return i because otherwise the function is pointless; even if you print the value, you'd need a way to use the string length, and you can't do that if you don't return it from the function.
Corrected code with example:
#include <stdio.h>
int mystrlen(const char *s)
{
int i;
for (i = 0; s[i] != '\0'; ++i);
return i;
}
int main(void)
{
const char *s = "Hello world!";
int len = mystrlen(s);
printf("Length of string: %d\n", len);
return 0;
}
First of all, using pointers, as the number says, consist in using a reference instead of the actual variable, so if you pass a reference by parameter, you are not passing the actual value of it, just an address to it!
The correct code is:
#include <stdio.h>
int mystrlen (const char *s)
{
int i;
for( i = 0; s[i] != '\0'; ++i);
printf("Length of string: %d\n", i);
return 0;
}
void main(){
char *string = "Hello World";
mystrlen(string);
}
Another thing to point out is that you're trying to declare a const variable and change it. When you declare a const variable it should not change.