I'm new to C and I try to make a program counting words in a sentence given as arguments when running the program. A word is a character or several seperated by either: ' ', '\n', ',' or '.'. Example: ./words abc abc = 2 words
But I keep getting: "segementation fault(core dumped)". Following is code:
int main(char **argv)
{
char buf[80];
sprintf(buf,"%d words\n",words(argv));
write(1,buf,strlen(buf));
return 0;
}
int words(char **argv)
{
int i=0, sum=0;
while(argv[i] != '\0')
{
if(argv[i] == '.' || argv[i] == ',' || argv[i] == ' ' || argv[i] == '\n')
sum++;
i++;
}
}
Argv is a **char or a pointer to an array of strings. Your code is treating it as if it is a single string so is looping through the pointers to strings and counting them. As none of these pointers is null the program continues beyond the end of the array causing a segfault.
me.c:2:5: warning: first argument of âmainâ should be âintâ [-Wmain]
me.c:2:5: warning: âmainâ takes only zero or two arguments [-Wmain]
me.c: In function âmainâ:
me.c:4:1: warning: implicit declaration of function âwordsâ [-Wimplicit-function-declaration]
me.c:5:1: warning: implicit declaration of function âwriteâ [-Wimplicit-function-declaration]
me.c:5:1: warning: implicit declaration of function âstrlenâ [-Wimplicit-function-declaration]
me.c:5:13: warning: incompatible implicit declaration of built-in function âstrlenâ [enabled by default]
me.c: In function âwordsâ:
me.c:11:16: warning: comparison between pointer and integer [enabled by default]
me.c:11:33: warning: comparison between pointer and integer [enabled by default]
me.c:11:51: warning: comparison between pointer and integer [enabled by default]
me.c:11:69: warning: comparison between pointer and integer [enabled by default]
gcc -Wall filename.c will produces all of the above warnings
These are the warnings in your code. Avoid all. and then try.
Please found Answers for these By searching on google
how to use command line arguments
how to declare a function
how to compare strings and how '.' is differ from "."
If I am not mistaken, then the arguments are automatically split into separate character strings. This is why you get a pointer to a pointer (e.g char ** instead of char*). You are dereferencing the argv array only once. Try this:
while(argv[i] != NULL) {
i++;
}
Second, you wont be able to detect newlines that way, since by definition you cannot pass newline in the arguments. What you probably want to do is parse the input from stdin and invoke your program like this:
echo "abc abc" | ./words
or
./words < SOME_TEXT_FILE
Last but not least your words function does not return anything, it needs to return i:
int words(char** argv) {
//...
return i;
}
That is probably the reason why your program segfaults, since the return value of words() will be NULL and then sprintf will try to dereference the result of the function.
So the whole function needs to look somewhat like this:
int words(char** argv) {
int counter = 0;
while(char[i] != NULL) {
int j = 0;
while(char[i][j] != '\0') {
if(char[i][j] == '.') { //no need to check for '\n'...
counter++;
}
j++;
}
i++;
counter++;
}
return counter;
}
1. Iterate over argv.(having in mind that argv[i] is a pointer to char, argv[0] holds name of the program being executed, and last element of argv is NULL pointer.
2. use strtok function from string.h library to split argv[i] with " .,\n". (every time strtok returns non-NULL value you increment words count).
With some reading on command line arguments and strtok you can easily make it work with any arguments passed.
Related
I'm trying to create a C function to return a pointer to the char with the greatest ascii value in a string, but my function is returning 'H' instead of 'o'. I think it's something to do with the if statement in the for loop but I'm not sure what the problem is. Any help is appreciated. Thanks!
char * select_max(char str[]);
int main(void) {
printf("%c\n",select_max("Hello"));
printf("All tests passed successfully.\n");
}
char *select_max(char str[]){
int length = strlen(str);
if(length<1){//returns 0 if string length is less than one
printf("Invalid string.\n");
return 0;
}
char *max = str;
for(int i=0;i<length;i++){
if(str[i] > max){
max = str[i];
}
}
return *max;
}
Try adding a printout so that you can see your bug in action. For example, just before your if:
printf("%c > %c = %d\n", str[i], max, (str[i] > max));
I believe this will quickly reveal the bug. [Hint: there are 5 bugs in your program.]
Compiler is nice enough to give you quite useful warnings, you should follow them, read carefully and try to understand. It won't point out your logical errors but you can deduce these too in this case.
main.c: In function 'main':
main.c:4:5: warning: implicit declaration of function 'printf' [-Wimplicit-function-declaration]
printf("%c\n",select_max("Hello"));
^~~~~~
main.c:4:5: warning: incompatible implicit declaration of built-in function 'printf'
main.c:4:5: note: include '<stdio.h>' or provide a declaration of 'printf'
main.c: In function 'select_max':
main.c:10:18: warning: implicit declaration of function 'strlen' [-Wimplicit-function-declaration]
int length = strlen(str);
^~~~~~
main.c:10:18: warning: incompatible implicit declaration of built-in function 'strlen'
main.c:10:18: note: include '<string.h>' or provide a declaration of 'strlen'
You first need to include the libraries that you use functions from, printf is from stdio.h and strlen is from string.h.
main.c:22:12: warning: returning 'char' from a function with return type 'char *' makes pointer from integer without a cast [-Wint-conversion]
return *max;
^~~~
You appear to be confusing chars with char*s. Since select_max is logically supposed to return just a character, (and you are already returning a char) declaring it as below will suffice.
#include<stdio.h>
#include<string.h>
char select_max( char str[] );
main.c:19:17: warning: assignment to 'char *' from 'char' makes pointer from integer without a cast [-Wint-conversion]
max = str[i];
And in the implementation of select_max, there is a similar problem. The temporary variable to hold the highest character is of type char* where it just needs to be a char:
char select_max(char str[]){
// ^___
int length = strlen(str);
if(length<1){//returns 0 if string length is less than one
printf("Invalid string.\n");
return 0;
}
// Initialize it to 0, has to be lower than any ASCII letters in order your algorithm to work. DO NOT leave it uninitialized.
char max = 0; // <---
for(int i=0;i<length;i++){
if(str[i] > max){
max = str[i];
}
}
return max;
}
The code above defines max as a char and initializes it to 0. Assigning str to it was pointless anyway. You are iterating through your string one character at a time and storing the character in a temporary container.
Note that the code in the for loop had already written as if it was of type char.
The same algorithm of course can be implemented in various ways but I suppose this was what you were trying to do.
what I have is a string that I get from txt file.
fgets(num_string, lenght, file);
num_string = "011110110101"
So than I select the first number and display it:
printf("Number: %c", num_string[0]);
After that my program gets all the numbers in string with this loop and it should then check each number if its 0 or 1:
for(j=0; j<=11; j++){
printf("numbers: %c\n", num_string[j]);
if(strcmp(num_string[j], zero)==0){
num_of_zeros++;
printf("\nNum of zeros: %d", num_of_zeros);
}
else{
num_of_ones++;
printf("\nNum of ones: %d", num_of_ones);
}
}
But the if statement does not want to work. Here is the problem that it writes in terminal:
AOC_2021_day_3.c: In function 'main':
AOC_2021_day_3.c:27:27: warning: passing argument 1 of 'strcmp' makes pointer from integer without a cast [-Wint-conversion]
if(strcmp(num_string[j], zero)==0){
^~~~~~~~~~
In file included from AOC_2021_day_3.c:3:0:
c:\mingw\include\string.h:77:38: note: expected 'const char *' but argument is of type 'char'
_CRTIMP __cdecl __MINGW_NOTHROW int strcmp (const char *, const char *) __MINGW_ATTRIB_PURE;
^~~~~~
I would appreciate any help :D
strcmp is for comparing to cstrings. In this case, you do not need strcmp at all. You want to check whether a specific character inside num_string is 0 or not. The if(strcmp(num_string[j], zero)==0) statement can be replaced with if(num_string[j] == '0').
If successful, the gets function returns its argument. But when I use the return value in printf, it occurs segmentation fault. The code that occurs error is below.
#include <stdio.h>
#define MAX_LINE 100
int main(void)
{
char line[MAX_LINE];
char *result;
printf("Please enter a string:\n");
if ((result = gets(line)) != NULL)
printf("The string is: %s\n", result); // Segmentation fault.
else if (ferror(stdin))
perror("Error");
}
/*** output ***
Please enter a string:
abc
Segmentation fault (core dumped)
*/
But if I use the argument of the gets function to print input, the error goes away.
/* -- snip -- */
printf("The string is: %s\n", line); // correct
In the case of the fgets function, there is no error when using the return value. Below is the fgets function code.
#include <stdio.h>
#define MAX_LEN 100
int main(void)
{
FILE *stream;
char line[MAX_LEN], *result;
stream = fopen("mylib/myfile","rb");
if ((result = fgets(line,MAX_LEN,stream)) != NULL)
printf("The string is %s\n", result); // correct
if (fclose(stream))
perror("fclose error");
}
If successful both function gets and fgets, both functions return the first argument char *str. Why would segmentation fault happen only in the gets function?
Above code is from https://www.ibm.com/docs/en/i/7.3?topic=functions-gets-read-line and https://www.ibm.com/docs/en/i/7.3?topic=functions-fgets-read-string#fgets
My environment is gcc 8.4.0.
Below is a warning message.
test.c: In function ‘main’:
test.c:11:18: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
if ((result = gets(line)) != NULL)
^~~~
fgets
test.c:11:16: warning: assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
if ((result = gets(line)) != NULL)
^
/tmp/ccuOAqAp.o: In function `main':
test.c:(.text+0x30): warning: the `gets' function is dangerous and should not be used.
(base) sh#sh-550P5C-550P7C:~/exercises/dummies/cpp$ gcc test.c
test.c: In function ‘main’:
test.c:11:18: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
if ((result = gets(line)) != NULL)
^~~~
fgets
test.c:11:16: warning: assignment to ‘char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
if ((result = gets(line)) != NULL)
^
/tmp/ccQIks7u.o: In function `main':
test.c:(.text+0x30): warning: the `gets' function is dangerous and should not be used.
gets has been removed from the C11 standard. Unfortunately the way it is implemented can cause the exact problem in this question. The gets declaration was removed from the stdio.h header but the definition/implementation is still present in libc. That's essentially what this warning is telling you (and illustrates why you should never ignore warnings):
test.c: In function ‘main’:
test.c:11:18: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
if ((result = gets(line)) != NULL)
For implicit declarations C assumes the function returns int. But in this case the function actually returns char *. For a 64 bit binary int is 32 bit and char * is 64 bit. Hence the return value will be incorrectly truncated and result in a seg fault when the invalid address is accessed.
The bottom line - don't use gets. Use fgets instead. See: Why is the gets function so dangerous that it should not be used?
If you really must use gets then the workaround is to compile with -std=c99 or manually declare gets prototype.
I call my function from my main as so...
int main() {
char* input[81];
char** array[81];
userInput(input);
parseInput(input);
}
userInput is taking in input and storing in the input variable. this is working correctly.
but now I want to send both the input and a char pointer array to my parseInput function to parse the input into an array using strtok
the following is my parseInput() function...
void parseInput(char* in) {
char *tok = strtok(in, " ");
int i = 0;
while(tok != NULL) {
array[i] = tok;
tok = strtok(NULL, " ");
i++;
}
}
when I try to compile I am getting the following warning and I do not understand why.
warning: passing argument 1 of ‘userInput’ from incompatible pointer type [enabled by default]
userInput(input);
warning: passing argument 1 of ‘parseInput’ from incompatible pointer type [enabled by default]
parseInput(input, array);
Any guidance would be greatly appreciated. thank you
You defined the function parseInput as taking a parameter of type char*, but in the function main, you are passing it a decayed parameter of type char**. That is why your compiler is complaining about an incompatible pointer type.
Instead of
char* input[81];
you should probably write:
char input[81];
Also, the declaration
char** array[81];
should probably be changed to:
char* array[81];
Also, as already pointed out in the comments section, the function parseInput will not compile, because it refers to an identifier array, but no identifier with that name is visible to that function. It cannot see the identifier array of the function main. Therefore, if you want the function parseInput to have access to array in the function main, then, when calling parseInput, you must pass a pointer to that array.
An additional problem with your code is that the behavior of the function parseInput does not make sense, as the caller of that function (in this case main) has no way of knowing how many elements of the pointer array were written by parseInput. Therefore, it has no way of knowing how many elements of the array are valid after the function call. For this reason, it would probably make more sense to make the function parseInput return an int which specifies the number of array elements written.
Another problem is that the function parseInput simply assumes that the passed array is large enough. If that assumption is false, then it will cause a buffer overflow. Therefore, it would probably be safer to design the function parseInput in such a way that it is also passed an argument which specifies the size of the passed array, so that the function can stop before a buffer overflow occurs.
After applying all of the modifications mentioned above, the function parseInput would look like this:
int parseInput( char* in, char** results, int results_length )
{
char *tok = strtok(in, " ");
int i = 0;
while( tok != NULL && i < results_length )
{
results[i] = tok;
tok = strtok(NULL, " ");
i++;
}
return i;
}
Instead of calling the function like this
parseInput(input);
you would now call it like this:
int main(void)
{
char input[81] = "This is a test.";
char* array[81];
int num_elements;
num_elements = parseInput( input, array, sizeof array / sizeof *array );
for ( int i = 0; i < num_elements; i++ )
{
printf( "%s\n", array[i] );
}
return 0;
}
This test program provides the following output:
This
is
a
test.
I'm getting this error:
$ gcc -Wall -g translate.c support.c scanner.c -o translate
support.c: In function ‘translate’:
support.c:148:13: warning: passing argument 1 of ‘strcmp’ from incompatible pointer type [enabled by default]
compareNum = strcmp(dict[i], token);
^
In file included from /usr/include/stdio.h:29:0,
from support.c:1:
/usr/include/string.h:28:6: note: expected ‘const char *’ but argument is of type ‘char **’
int _EXFUN(strcmp,(const char *, const char *));
^
and here is the function translate()
int
translate(char* token, char** dict[], int dictLength)
{
int i = 0;
int compareNum;
for(i=0;i<dictLength;i++)
{
if(i % 2 == 0)
{
compareNum = strcmp(dict[i], token);
++i;
if(compareNum == 1)
{
return i;
}
}
}
return 0;
}
for some reason I'm passing in dict[i], which is an array of strings that I'm trying to compare each even element of the array to the string token but its saying its char**. I know the array is char** but wouldn't the element be char *?
The dict argument is declared like so:
char** dict[]
So dict[i] is of type char**. Hence the error.
I guess that in order for us to offer further advice we'd need to have some details of the object that you supply as the dict argument when calling this function. Perhaps you simply need to change the declaration of the argument to be:
char* dict[]
One thing that I would recommend strongly would be to use const when declaring these parameters, to allow you to pass non-modifiable strings.
You have dict parameter declared as:
char** dict[]
Which is an array of char**.
This means dict[index] is a char**
Changing the parameter
char** dict[]
to
char* dict[]
should fix your problem