Why is the second argument not working with strtol? - c

I did this something like this:
/* convert the argv[1] into Integer and store the result in key
* using library function: strtol() to do it */
char **flag = NULL;
key = strtol(argv[1], flag, 10);
// if argv[1] is not all digits
if (**flag != '\0')
{
printf("Usage: ./caesar key\n");
return 1;
}
But it throws a segmentation fault. I am not sure why.
In the C documentation, strtol is prototyped as long int strtol(const char *nptr, char **endptr, int base). Why am I getting a segmentation fault?
And when change some parts of the code to char *flag, strtol(argv[1], &flag, 10), and if (*flag != '\0'), everything works as expected.
I understand (sort of) how making the edits corrects the code. However, I do not know why the original code does not work. Does anyone have a clue?

You need to pass the address of your char * pointer, so that strtol can update it.
char *flag = NULL;
key = strtol(argv[1], &flag, 10);
// if argv[1] is not all digits
if (*flag != '\0')
After the call, the pointer will be modified to point to the end of the parsed region of the input string.
The man page wording is admittedly confusing here.

I do not know why the original code does not work? Does anyone have any clue?
Because here
char **flag = NULL;
flag is set to NULL, then here
key = strtol(argv[1], flag, 10);
flags's value (NULL) is passed to strtol(), which does not change flags's value in any way, it is NULL before and after the call to strtol().
and finally here
if (**flag != '\0')
flag, which has the value NULL, is dereferenced, which invokes undefined behaviour, which could lead to anything, which in your case is a crash.

Consider this simple function which multiplies a number by 2.
void Times2(int number, int *result)
{
*result = number * 2;
}
You should call it like this:
int result;
Times2(3, &result);
printf("%d\n", result);
and the expected output would be 4.
But if you call it like this (which is basically what you're doing when you call strtol in your buggy code):
int *result = NULL;
Times2(3, result);
printf("%d\n", result);
your program will most likely end up with a segmentation fault, because in the Times2 function you dereference a NULL pointer.

Why second argument not working with strol?
char **flag = NULL;
key = strtol(argv[1], flag, 10);
It is working. Passing is a null pointer is fine, but flag will not get changed in the calling code.
When the 2nd argument is a null pointer, nothing is updated about where the conversion ended.
Later code: *flag in if (**flag != '\0') is bad (undefined behavior) as that attempts to de-reference a null pointer.
Instead pass the address of a known pointer:
//char **flag = NULL;
char *flag;
//key = strtol(argv[1], flag, 10);
key = strtol(argv[1], &flag, 10);
// if (**flag != '\0')
if (*flag != '\0')
Following is a complete test conversion of a char* to an int.
#include <stdlib.h>
#incluse <ctype.h>
// Return error flag
int my_strtol(long *destination, const char *s) {
if (destination == NULL) {
return 1;
}
if (s == NULL) {
*destination = 0;
return 1;
}
char *endptr;
errno = 0;
*destination = strtol(s, &endptr, 0);
// If no conversion or out-of-range
if (s == endptr || errno == ERANGE) {
return 1;
}
// I like to tolerate trailing white-space.
while (isspace(*(const unsigned char *) endptr)) {
endptr++;
}
return *endptr != '\0';
}

Related

strsep() causing Segmentation fault

I am having a problem with my program in which I am getting a segmentation fault from strsep() which was gotten from GDB and has the error message
Program received signal SIGSEGV, Segmentation fault.
0x00002aaaaad64550 in strsep () from /lib64/libc.so.6
My code is as follows:
int split(char *string, char *commands, char *character) {
char **sp = &string;
char *temp;
temp = strdup(string);
sp = &temp;
for (int i = 0; i < 100; i++) {
commands[i] = strsep(sp, character);
if (commands[i] == '\0') {
return 0;
}
if (strcasecmp(commands[i], "") == 0) {
i--;
}
printf("%d", i);
}
return 0;
}
Any help would be greatly appreciated as I have spent hours trying to solve this problem
The arguments for the function are ("Hello World", "#", "&")
EDIT
So I have managed to get rid of the segment fault by changing the code to
int split(char* string, char* commands, char* character) {
for(int i = 0; i < 100; i++) {
commands[i] = strsep(&string, character);
if(commands[i] == '\0') {
return 0;
}
if(strcasecmp(&commands[i], "") == 0) {
i--;
}
}
return 0;
}
However now I have a new problem with commands returning a null array where every index is out of bounds.
EDIT 2
I should also clarify on what I am trying to do a bit so essentially commands is of type char* commands[100] and I want to pass it into the function when then modifies the original pointer array and store say `"Hello World"' into commands[0] then I want to modify this value outside the function.
Your usage of commands is inconsistent with the function prototype: the caller passes an array of 100 char*, commands should be a pointer to an array of char *, hence a type char **commands or char *commands[]. For the caller to determine the number of tokens stored into the array, you should either store a NULL pointer at the end or return this number or both.
Storing commands[i] = strsep(...) is incorrect as commands is defined as a char *, not a char **.
It is surprising you get a segmentation fault in strsep() because the arguments seem correct, unless character happens to be an invalid pointer.
Conversely you have undefined behavior most likely resulting in a segmentation fault in strcasecmp(commands[i], "") as commands[i] is a char value, not a valid pointer.
Here is a modified version:
// commands is assumed to point to an array of at least 100 pointers
// return the number of tokens or -1 is case of allocation failure
int split(const char *string, char *commands[], const char *separators) {
char *dup = strdup(string + strcspn(string, separators));
if (temp == NULL)
return -1;
char *temp = dup;
char **sp = &temp;
int i = 0;
while (i < 99) {
char *token = strsep(sp, separators);
if (token == NULL) // no more tokens
break;
if (*token == '\0') // ignore empty tokens
continue;
commands[i++] = token;
}
commands[i] = NULL;
if (i == 0) {
free(dup);
}
return i;
}
The memory allocated for the tokens can be freed by freeing the first pointer in the commands array. It might be simpler to duplicate these tokens so they an be freed in a more generic way.

using strtol on a string literal causing segmentation fault

I have a string that I get by getline() (more precisely I use a loop and getline to read a file line by line)
Let's say the line is 12|34|
Then I use strtok() to cut it down by substr = strtok(line, "|");
and store them into a string array with a loop, part[index] = substr;
So the part[0] here should be "12" and part[0] is "34"
I would like to use strtol, but I have checked that it can't be used on string literal, then I try following code.
char *temp = strdup(part[1]);
char **ptr;
long ret = strtol(temp, ptr, 10);
printf("%x\n", ret);
and when I read the second line, it causes segmentation fault. By how can I really use strtol to convert the string into integer
the issue is that ptr isn't initialised. So when strtol tries to write at the address ptr, it crashes (or undefined behaviour).
You have to pass the valid address of a pointer to store the last unprocessed char, like:
char *ptr;
long ret = strtol(temp, &ptr, 10);
&ptr is valid and points to the auto variable storage location to ptr
You're misusing strtol. It takes a char** because it intends to set the char* it points to, per the man page:
If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr.
By passing it an uninitialized char** you invoke undefined behavior when it tries to dereference it. Change the code to:
char *ptr; // Place to put the end ptr
long ret = strtol(temp, &ptr, 10); // Pass address of the location it can set
Alternatively, if you never use ptr, just do:
long ret = strtol(temp, NULL, 10); // Don't care about end ptr; strtol won't set on NULL
char **ptr;
long ret = strtol(temp, ptr, 10);
is wrong. ptr is not initialized and doesn't refer to anything useful.
The second parameter of strtol() must refer to the address of an actual char * value that stores the address of the first non-converted character. Per 7.22.1.3 The strtod, strtof, and strtold functions of the C standard:
A pointer to the final string is stored in the object pointed to by
endptr, provided that endptr is not a null pointer.
The proper code would be
char *endptr;
long ret = strtol(temp, &endptr, 10);
or
char *endptr;
char **ptr = &endptr;
long ret = strtol(temp, ptr, 10);
In this case, after the call to strtol() the value in endptr would be the address of the first character that wasn't converted to the resulting long value.
If you don't care what the first non-converted character is:
char **ptr;
long ret = strtol(temp, NULL, 10);
Function strtol(const char *str, char **str_end, int base); will dereference str_end and will do something like *str_end = one_after_end_of_parsed_long; So when you pass a pointer of type char**, which does not point to a valid pointer object that can be modified by strtol then, you'll yield undefined behaviour.
You'd rather write
char *ptr; // space for taking on a pointer value
long ret = strtol(temp, &ptr, 10);
or (not the preferred variant):
char **ptr = malloc(sizeof(char*));
long ret = strtol(temp, ptr, 10);
...
free(*ptr);
Here you already have separated your string.
So each string contains one long number. The second argument is used to know where the conversion stopped in the string.
If you don't need it, pass in NULL
char *temp = strdup(part[1]);
long ret = strtol(temp, NULL, 10);
printf("%lx\n", ret);
In addition printf for long number requires different format flags. Here lx for long hexadecimal.
There is absolutely no need to use strtok() at all, because strtol() sets the pointer pointed by the second parameter to point to the character following the number parsed.
A complete example program:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int main(void)
{
char *line_ptr = NULL;
size_t line_max = 0;
ssize_t line_len;
long *number = NULL;
size_t numbers = 0;
size_t numbers_max = 0;
char *curr, *next, *ends;
long temp;
size_t i;
while (1) {
line_len = getline(&line_ptr, &line_max, stdin);
if (line_len < 1)
break;
curr = line_ptr;
ends = line_ptr + line_len;
numbers = 0;
while (1) {
/* Parse next long. */
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break;
if (next == curr)
break;
/* Need to grow number array first? */
if (numbers >= numbers_max) {
size_t temp_max = (numbers | 1023) + 1025 - 16;
long *temp_ptr;
temp_ptr = realloc(number, temp_max * sizeof number[0]);
if (!temp_ptr) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
numbers_max = temp_max;
number = temp_ptr;
}
/* Save parsed number. */
number[numbers++] = temp;
/* Skip trailing whitespace, */
curr = next;
while (curr < ends && (*curr == '\t' || *curr == '\n' || *curr == '\v' ||
*curr == '\f' || *curr == '\r' || *curr == ' '))
curr++;
/* Skip separator. */
if (*curr == '|')
curr++;
else
break; /* No separator, so that was the final number. */
}
printf("Parsed %zu longs:", numbers);
for (i = 0; i < numbers; i++)
printf(" %ld", number[i]);
printf("\n");
fflush(stdout);
}
if (ferror(in)) {
fprintf(stderr, "Error reading standard input.\n");
exit(EXIT_FAILURE);
}
free(line_ptr);
line_ptr = NULL;
line_max = 0;
free(number);
number = NULL;
numbers = 0;
numbers_max = 0;
return EXIT_SUCCESS;
}
Other than the available memory, this program has no limits wrt. line length or the amount of numbers it stores in the array. The growth policy for the number array is funky (just my style); feel free to replace it with anything you prefer. Just make sure temp_max is at least numbers + 1. Making it larger means you allocate more at once, and therefore do fewer "slow" realloc() calls.
The outer while loop iterates over lines read from standard input.
The inner while loop parses the longs from that line, separated by a pipe character |. strtol() ignores leading whitespace. In case there is whitespace between the number and the following pipe character, we need to explicitly skip that; you could also use just while (curr < ends && isspace(*curr)) curr++; for that.
If you want to collect all the longs into a single array, rather than per line, just omit the numbers = 0; before the inner while loop. (And move printing out the numbers after the outer while loop.)
The actual conversion,
next = curr;
errno = 0;
temp = strtol(curr, &next, 0);
if (errno)
break; /* errno == ERANGE; number too large in magnitude! */
if (next == curr)
break; /* end of input, no number */
relies on the fact that if the number to be converted is too large in magnitude, strtol() will set errno = ERANGE and return LONG_MIN (if the number in the string was negative) or LONG_MAX (if positive). To detect that, we must set errno to zero first. If the string is empty (or there is a stray nul char, \0, in the line), strtol() will return 0 with next == curr.

I'am getting a Segmentation fault while using fgetc(). How can I fix it?

I want to read from a filestream for z bytes. Then I want to give the value in a char array back. I would appreciate it if you could give me an explaination. That's my code since now:
char * getData(FILE * fp, long * x)
{
int z = 0;
char * data = malloc(sizeof(char) * BUFFLENGTH);
strcpy(data,"");
while(z < BUFFLENGTH-2)
{
if(feof(fp) == 0)
{
data[z] = fgetc(fp);
z++;
x++;
}
else
{
strcat(data,"\0");
return data;
}
}
}
I know that the segmentation fault is triggered threw this line:
data[z] = fgetc(fp);
But I dont know why.
You should check if the allocation was successful.
You should check if the reading was successful after calling fgetc() instead of using feof().
This usage of strcat() will invoke undefined behavior if one or more bytes are read from the file and no byte its value is zero is read because a pointer pointing something which is not null-terminated string will be passed to strcat() and it will access area allocated via malloc() and not initialized. Instead of this, you should terminate the string by adding '\0'.
You should return something explicitly even if the length read exceeds the limit, otherwise you will invoke undefined behavior if the caller use the return value.
code with these advices applied:
char * getData(FILE * fp, long * x)
{
int z = 0;
char * data = malloc(sizeof(char) * BUFFLENGTH);
if(data == NULL) return NULL;
/* strcpy() isn't needed because it will be overwritten */
while(z < BUFFLENGTH-2)
{
int input = fgetc(fp);
if(input != EOF)
{
data[z] = input;
z++;
x++;
}
else
{
data[z] = '\0';
return data;
}
}
free(data);
return NULL;
}

Basic C cast warning pointer from int

Can someone tell me how to correct this warning/error. I am trying to get just the first character of a string to tell if it a "-".
Error:
grep-lite.c:15:13: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
if(strcmp((char *) pattern[0],"-") == 0)
^
grep-lite.c:29:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
while(strcmp((char *) argv[index][0],"-"))
^
Source with warnings/errors:
Line 15:
if (strcmp((char *) pattern[0],"-") == 0)
Line 29:
while (strcmp((char *) argv[index][0],"-"))
Complete Source:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "grep-lite.h"
int main(int argc, char * * argv)
{
//initailize variables
int index = 1, lineNumber = 1;
int oneMatchingLine = FALSE;
int invertOpt = FALSE, lineNumOpt = FALSE, quietOpt = FALSE;
char * pattern = argv[argc];
//check if last arguement is invalid by starting with a '-'
if(strcmp((char *) pattern[0],"-") == 0)
{
error(INVALID_PATTERN);
return EXIT_ERROR;
}
//check if they asked for help
if(strcmp(argv[index],"--help") == 0)
{
printHelpStatement();
return EXIT_SUCCESS;
}
//walk through all options
while(strcmp((char *) argv[index][0],"-"))
{
//find and set option
if(processOptions(argv[index], &invertOpt, &lineNumOpt, &quietOpt))
index++;
//if invalid option fai
else
{
error(INVALID_OPTION);
return EXIT_ERROR;
}
}
//walk through stdinput searching for pattern relationship
while(feof(stdin) == 0)
{
//initialize
char * thisLine = NULL;
// get read line with fgets
thisLine = fgets(thisLine, MAX_CHARACTERS, stdin);
//find pattern location in thisLine
char * patternLoc = strstr(thisLine, pattern);
//check if we should print this line based of patternLoc and invertOpt
if((!patternLoc != NULL && !invertOpt) || (pattenLoc == NULL && invertOpt))
{
//see if we should print this line
if(!quietOpt)
printLine(thisLine, lineNumOpt, lineNumber);
}
lineNumber++;
}
I will enumerate the problems I find in your code
The correct usage of strcmp() exists in your code, in this line
if (strcmp(argv[index],"--help") == 0)
strcmp() is intended for string comparison, not character comparison, this
if(strcmp((char *) pattern[0],"-") == 0)
should be
if (pattern[0] == '-')
do not cast the variable to force compilation, instead find the actual cause for the compiler error/warning.
You have a severe error, you don't allocate space for the thisLine char pointer, you must allocate memory via malloc() or just declare it as a char array like
char thisLine[SOME_CONSTANT_NUMBER];
Also, this
while(feof(stdin) == 0)
is never a good idea instead do it like this
char thisLine[100];
while (fgets(thisLine, sizeof(thisLine), stdin) != NULL)
You made another very common mistake, arrays in c are indexed from 0 to N - 1, so
char *pattern = argv[argc]
is wrong, because you are reading one element after the last, the correct code is
char *pattern = argv[argc - 1]
which will give you the last element.

Reading stream char by char

I'm using this function to read, char by char, a text file or a stdin input
void readLine(FILE *stream, char **string) {
char c;
int counter = 0;
do {
c = fgetc(stream);
string[0] = (char *) realloc (string[0], (counter+1) * sizeof(char));
string[0][counter++] = c;
} while(c != ENTER && !feof(stream));
string[counter-1] = '\0';
}
But when I call it, my program crashed and I really don't know why, because I don't forget the 0-terminator and I'm convinced that I stored correctly the char sequence. I've verified the string length, but it appears alright.
This is an error:
do {
c = fgetc(stream);
// What happens here?!?
} while(c != ENTER && !feof(stream));
"What happens here" is that you add c to string before you've checked for EOF, whoops.
This is very ungood:
string[0] = (char *) realloc (string[0], (counter+1) * sizeof(char));
in a loop. realloc is a potentially expensive call and you do it for every byte of input! It is also a silly and confusing interface to ask for a pointer parameter that has (apparently) not been allocated anything -- passing in the pointer usually indicates that is already done. What if string were a static array? Instead, allocate in chunks and return a pointer:
char *readLine (FILE *stream) {
// A whole 4 kB!
int chunksz = 4096;
int counter = 0;
char *buffer = malloc(chunksz);
char *test;
int c;
if (!buffer) return NULL;
while (c = fgetc(stream) && c != ENTER && c != EOF) {
buffer[counter++] = (char)c;
if (counter == chunksz) {
chunksz *= 2;
test = realloc(buffer, chunksz);
// Abort on out-of-memory.
if (!test) {
free(buffer);
return NULL;
} else buffer = test;
}
}
// Now null terminate and resize.
buffer[counter] = '\0';
realloc(buffer, counter + 1);
return buffer;
}
That is a standard "power of 2" allocation scheme (it doubles). If you really want to submit a pointer, pre-allocate it and also submit a "max length" parameter:
void *readLine (FILE *stream, char *buffer, int max) {
int counter = 0;
int c;
while (
c = fgetc(stream)
&& c != ENTER
&& c != EOF
&& counter < max - 1
) buffer[counter++] = (char)c;
// Now null terminate.
buffer[counter] = '\0';
}
There are a few issues in this code:
fgetc() returns int.
Don't cast the return value of malloc() and friends, in C.
Avoid using sizeof (char), it's just a very clumsy way of writing 1, so multiplication by it is very redundant.
Normally, buffers are grown more than 1 char at a time, realloc() can be expensive.
string[0] would be more clearly written as *string, since it's not an array but just a pointer to a pointer.
Your logic around end of file means it will store the truncated version of EOF, not very nice.
Change this line
string[counter-1] = '\0';
to
string[0][counter-1] = '\0';
You want to terminate string stored at string[0].

Resources