Why am I getting segmentation fault for this program? - c

I have to write a program which reads dates and todo-s as command line arguments and puts these in a text-file (line by line).
Correct input:
./todo 13:58 todo1 00:00 todo2 08:30 todo3
Wrong input:
./todo 13:58 todo1 24:00 todo2 08:30 todo3 or ./todo 13:58 todo1 todo2 00:00 08:30 todo3
Correct todolist.txt file:
13:58 - todo1
00:00 - todo2
08:30 - todo3
The code is compiled successfully, but when I run the program, I get segmentation fault:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
bool isValidTime(char* argv)
{
char hour[2] = {0};
char minute[2] = {0};
for (unsigned int i = 0; argv[i] != ':'; i++) {
hour[i] = argv[i];
}
int hourNum = atoi(hour);
if (!(hourNum >= 0 && hourNum < 24)) {
return false;
}
for (unsigned int i = 3; i < sizeof(*argv) / sizeof(argv[0]); i++) {
minute[i] = argv[i];
}
int minuteNum = atoi(minute);
if (!(minuteNum >= 0 && minuteNum < 60)) {
return false;
}
return true;
}
bool isValidTodo(char* argv)
{
for (unsigned int i = 0; i < sizeof(*argv) / sizeof(argv[0]); i++) {
if (!(argv[i] >= 'a' && argv[i] <= 'z')) {
return false;
}
}
return true;
}
int main(int argc, char* argv[])
{
if (argc < 3) {
printf("Incorrect input!\n");
return 1;
}
FILE* file;
file = fopen("todolist.txt", "w");
if (file == NULL) {
printf("File failed to open.\n");
return 1;
}
for (int i = 1; i < argc; i++) {
if (!(isValidTime(argv[2 * i - 1]) && isValidTodo(argv[2 * i]))) {
printf("Incorrect input!\n");
}
fprintf(file, "%s - %s\n", argv[2 * i - 1], argv[2 * i]);
}
fclose(file);
return 0;
}

Most likely it is because of this code:
for (int i = 1; i < argc; i++) {
if (!(isValidTime(argv[2 * i - 1]) && isValidTodo(argv[2 * i]))) {
printf("Incorrect input!\n");
}
fprintf(file, "%s - %s\n", argv[2 * i - 1], argv[2 * i]);
}
You are getting argc number of arguments, but then you index the array using argv[2 * i - 1]. Say you have 4 arguments - you are going to try to index element 2 * 4 - 1, that is, element 7 which would be outside the array, hence the segmentation fault.
You probably need to just remove the 2 * part, if your goal is to do something with the previous and current elements.
Another issue I see is this code:
for (unsigned int i = 0; argv[i] != ':'; i++) {
hour[i] = argv[i];
}
In case there is no : in the argument, you will loop indefinitely and will get another segmentation fault for hour[i] and argv[i].

Related

CS50 Caesar segmentation fault

I am new here and working on the second homework Caesar of cs50, it seems most of my review is correct except the last one -- I cannot handle the situation of lacking argv[1], which means if I only type ./caesar, it will return segmentation fault. I am wondering why this code if (argc != 2) cannot return 0 when argc == 1, however it works when argc > 1, I find that is weird. Can anyone help me?? Thanks in advance!
# include <stdio.h>
# include <cs50.h>
# include <string.h>
# include <ctype.h>
# include <math.h>
# include <stdlib.h>
int check_the_key(int argc, string y);
int main(int argc, string argv[])
{
string x = argv[1];
int y = argc;
int k = check_the_key(y, x);
if (k == 0)
{
printf("ERROR!!!!!\n");
return 1;
}
else
{
// printf("The key is %i\n", k);
string text = get_string("Input your text:");
int i;
int n;
printf("ciphertext: ");
for (i = 0, n = strlen(text); i < n; i++)
{
if (islower(text[i]))
{
printf("%c", (text[i] - 97 + k) % 26 + 97 );
}
else if (isupper(text[i]))
{
printf("%c", (text[i] - 65 + k) % 26 + 65);
}
else
{
printf("%c", text[i]);
}
}
printf("\n");
return 0;
}
}
int check_the_key(int argc, string y)
{
int number = argc;
string key = y;
int numberkey = atoi(key);
if (argc != 2)
{
return 0;
}
else
{
if (numberkey > 0)
{
return numberkey;
}
else
{
return 0;
}
}
}
I know what is going on! Because I need to pass some value into atoi(), if I only call ./caesar, there is no value I can pass into atoi(), so it causes segmentation fault. Which means I need to change code order slightly, put int numberkey = atoi(key); inside the else loop. So the code will run if (argc != 2) first, if no, then go to the next step! Here is the code after change.
int check_the_key(int argc, string y)
{
int number = argc;
string key = y;
if (argc != 2)
{
return 0;
}
else
{
int numberkey = atoi(key);
if (numberkey > 0)
{
return numberkey;
}
else
{
return 0;
}
}
}

I need to calculate numbers from argv input, but the numbers aren't integers they are chars. How do i cast?

I cant calculate numbers that are not integers. i need a way to cast chars into strings to use atoi() function with, or at least a way to calculate these numbers
I tried subtracting 48/'0' from the char.
argv = ADD 2 3 5 7 11 13
#define MINIMUM_ARGS 2
int main(int argc, char** argv)
{
int i = 0;
int calc = 0;
char string[2] = { 0 };
if ((strcmp(argv[1], "SUB") == 0 || strcmp(argv[1], "ADD") == 0) && ((argc - MINIMUM_ARGS) > 1))
{
string[0] = argv[2]; //Making calc be the value of the first number.
calc = atoi(string);
for (i = 3; i < argc; i++)
{
if (strcmp(argv[1], "SUB") == 0)
{
if (strlen(argv[i]) > MINIMUM_ARGS)
{
calc -= atoi(argv[i]);
}
else
{
calc -= atoi(argv[i] - 48);
}
}
else
{
if (strlen(argv[i]) < MINIMUM_ARGS)
{
string[0] = argv[i];
calc += atoi(string);
}
else
{
calc += atoi(argv[i]);
}
}
}
printf("Result: %d", calc);
}
Expected = 41
Got = 0
Seems to me that you are making things more complicated than needed. I don't understand why MINIMUM_ARGS is used in the code that converts the input strings to numbers - atoi is all you need.
Perhaps like:
#define MINIMUM_ARGS 3
int main(int argc, char** argv)
{
int i = 0;
int calc = 0;
if ((strcmp(argv[1], "SUB") == 0 || strcmp(argv[1], "ADD") == 0) && (argc >= MINIMUM_ARGS))
{
calc = atoi(argv[2]);
for (i = 3; i < argc; i++)
{
if (strcmp(argv[1], "SUB") == 0)
{
calc -= atoi(argv[i]);
}
else
{
calc += atoi(argv[i]);
}
}
printf("Result: %d\n", calc);
}
}

Can't find and fix my seg fault error

"*Program received signal SIGSEGV, Segmentation fault.
0x000000000040073b in concat (nextstr=0x7fffffffee66 "the",
longstr=0x7fffffffe750 "", i=0x0) at main.c:24
24 printf("%c -> %c\n", nextstr[j], longstr[*i]);
(gdb)"
// define max for command line
#define MAX_CHARS 1000
#include <stdio.h>
int concat(char[], char[], int *);
void printreverse(char[], int);
int main(int argc, char *argv[])
{
char longstring[MAX_CHARS] = { '\0' };
int i = 0, j;
if (argc < 2)
{
printf("%s requires command-line args\n", argv[0]);
return 1;
}
for (j = 1; j <= argc; j++)
{
if (concat(argv[j], longstring, i))
return 1;
}
i--;
longstring[i] = '\0'; //delete trailing space
printf("%s\n", longstring);
}
int concat(char nextstr[], char longstr[], int *i)
{
int j = 0;
while (nextstr[j] != '\0')
{
printf("%c -> %c\n", nextstr[j], longstr[*i]);
longstr[*i] = nextstr[j];
(*i)++;
j++;
if (*i > MAX_CHARS)
{
printf("Error: Input is too long!\n");
return 1;
}
}
if (j > 0)
{
if ((*i) + 2 > MAX_CHARS)
{
printf("Error: Input is too long!\n");
return 1;
}
longstr[*i] = ' ';
longstr[(*i) + 1] = '\0';
(*i)++;
}
return 0;
}
Pointer error most likely
"This takes a command line argument is checks the length but there's a seg fault"
I think the error is on line 33
but I can't figure out how to fix it
The main problem that is causing the crash is in your for loop:
for(j = 1; j <= argc; j++){
if(concat(argv[j], longstring, i)) return 1;
}
Since concat's third parameter is an int *, you need to pass a pointer to i. Also, you are iterating through the loop one too many times. If argc is 2, then the program name is in argv[0], and your argument is in argv[1]. So, change the loop to this:
for(j = 1; j < argc; j++) {
if (concat(argv[j], longstring, &i))
return 1;
}

Strange behavior when using free in c program

I have some program which decompress some string which is already mentioned here: How to decompres array of char in c. After I finished it I have problem with function free (without it, it works ok). There is some strange behaviour and the last assert fails because of : Aborted; core dumped;
when I debug this program I found that problem is in this cycle:
for (j = 0; j < max - 1; j++) {
vysledek[index] = src[i - pom];
printf("cccc%d\n%s\n", j,vysledek);
printf("xxx%c", src[i - pom]);
index++;
}
it prints:
...
xxx#cccc19
HHeeeeeellllllllooooooooooooooo#####################�
xxx#cccc20
HHeeeeeellllllllooooooooooooooo######################
xxx#cccc21
HHeeeeeellllllllooooooooooooooo#######################
xxx#cccc22
HHeeeeeellllllllooooooooooooooo########################
xxx#cccc23
HHeeeeeellllllllooooooooooooooo#########################Hello______________________________world!
xxx#cccc24
HHeeeeeellllllllooooooooooooooo##########################ello______________________________world!
...
can someone explain me this ? How can Hello world from second assert discover in third one ?
whole program is here:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
char * decompress(const char * src) {
int max = 0;
int pom = 1;
int maxSize = 0;
int index = 0;
int isNumber = 0;
int i;
for (i = 0; src[i] != 0; i++) {
max = 0;
isNumber = 0;
while (isdigit(src[i])) {
int digit = (src[i] - '0');
max = max * 10 + digit;
i++;
isNumber = 1;
}
if (max == 0 && !isNumber) {
max = 1;
}
maxSize = maxSize + max;
}
char *vysledek = (char*) malloc((maxSize) * sizeof (int));
for (i = 0; src[i] != 0; i++) {
max = 0;
pom = 0;
isNumber = 0;
while (isdigit(src[i])) {
int digit = (src[i] - '0');
max = max * 10 + digit;
i++;
pom++;
isNumber = 1;
}
if (!isNumber) {
vysledek[index] = src[i];
//printf("%c", src[i]);
index++;
} else {
i--;
int j;
if (max < 1) {
index--;
}
for (j = 0; j < max - 1; j++) {
vysledek[index] = src[i - pom];
//printf("cccc%d\n%s\n", j,vysledek);
//printf("xxx%c", src[i - pom]);
index++;
}
}
//printf("\n%d\n", index);
}
return vysledek;
}
int main(int argc, char * argv []) {
char * res;
assert(!strcmp(
(res = decompress("Hello world!")),
"Hello world!"));
//free(res);
assert(!strcmp(
(res = decompress("Hello_30world!")),
"Hello______________________________world!"));
//free(res);
assert(!strcmp(
(res = decompress("H2e6l8o15 35w5o6r-2d0!!")),
"HHeeeeeellllllllooooooooooooooo wwwwwoooooor--!!"));
//free(res);
return 0;
}
The problem is, that you compare a null-terminated string with a not-null-terminated string.
In your function decompress() you need to reserve one more int and add the missing \0 to the copied buffer.
char *vysledek = (char*) malloc((maxSize) * sizeof (int) + sizeof(int));
[...]
vysledek[index] = '\0';

program adding numbers doesn't show any output

I have a problem with an "add calculator".
Valgrind reports no memory errors, no errors from compiler but the program doesn't show any output despite the printf - "Base is ".
All pointers, and variables are (n my opinion) correctly initialized.
getnum function gets a number, returns a pointer to char - char *,
add function processes two numbers as strings, returns result which is a pointer to char (char *) as well.
I don't know whether the problem is memory allocation or procedures connected to processing arrays...
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define MAX(A,B) ((A)>(B) ? (A) : (B))
char *getnum(FILE *infile, int base)
{
int len = 10;
int c;
int pos = 0;
char *num = NULL;
char *tmpnum = NULL;
num = malloc(sizeof(char)*len);
while (((c = fgetc(infile)) != EOF) && (isalnum(c))) {
if (isdigit(c)) {
/* irrelevant*/
}
else if (isalpha(c)) {
fprintf(stderr, "Wrong base, expected 16\n");
free(num);
return NULL;
}
if (pos >= len) {
/*realloc*/
}
}
return num;
}
int main(int argc, char **argv)
{
FILE *infile = NULL;
char *number1 = NULL;
char *number2 = NULL;
char *result = NULL;
int base, i, j = 0, length, count = 0;
infile = fopen(argv[1], "r");
base = atoi(argv[2]);
while (!feof(infile)) {
number1 = getnum(infile, base);
number2 = getnum(infile, base);
break;
}
printf("Base is %d\n", base);
result = add(number1, number2, base);
length = strlen(result);
for (i = 0; i <= length - 1; i++) {
if (result[i] == '0') {
count++;
}
}
for (j = i; j == (length - 1); j++) {
printf("Result is: %s\n", &result[j]);
break;
}
free(result);
result = NULL;
fclose(infile);
return 0;
}
Trying to work it out for the past 4 hours and can't find a mistake.
Thanks in advance!
There is one severe typo near the end of main().
for (j = i; j == (length - 1); j++) {
/* ^^ SHOULD BE <= */
printf("Result is: %s\n", &result[j]);
break;
}
Looking at this code:
for (i = 0; i <= length - 1; i++) {
if (result[i] == '0') {
count++;
}
}
if (count == length) {
printf("Result is 0\n");
free(result);
result = NULL; /* arguable */
fclose(infile);
return 0;
}
for (i = 0; i <= length - 1; i++) {
if (result[i] != '0') {
break;
}
}
for (j = i; j == (length - 1); j++) {
printf("Result is: %s\n", &result[j]);
break;
}
Instead of counting the total number of zeroes in the output number, and then counting the number of leading zeroes again, why not combine the two?
What is the last loop about? It's not even really a loop - it will execute once if i is length - 1, or not at all if not (presumably you're hitting the latter case in your test input).
e.g.
for (count = 0; count < length; count++) {
if (result[count] != '0')
break;
}
if (count == length) {
printf("Result is 0\n");
free(result);
result = NULL; /* arguable */
fclose(infile);
return 0;
}
printf("Result is: %s\n", &result[count]);

Resources