Hey I'm having trouble solving a bug that causes some unwanted NULL's to be displayed in my final output. When you run the program enter a string such as 'nnseeeewwwew' into the command line before running.
Here is the code
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
/* mutex */
pthread_mutex_t thisMutex;
/* Condition Variables */
sem_t nQueue, eQueue, sQueue, wQueue;
int nFirst, sFirst, eFirst, wFirst;
int done = 0;
/* Thread Prototypes */
void *bats(void *);
void *checking(void *);
/* Prototypes */
void arrive(int num, char *dir);
void cross();
void leave(int num, char *dir);
void check();
/* Global pointer to argv */
char *directions = NULL;
int main(int argc, char *argv[])
{
int i = 0,
n = 0,
s = 0,
e = 0,
w = 0;
directions = argv[1];
while( argv[1][i] == 'n' || argv[1][i] == 'e' ||
argv[1][i] == 's' || argv[1][i] == 'w' )
{
if(argv[1][i] == 'n'){n++;}
if(argv[1][i] == 's'){s++;}
if(argv[1][i] == 'e'){e++;}
if(argv[1][i] == 'w'){w++;}
i++;
}
pthread_mutex_init(&thisMutex, NULL);
sem_init(&nQueue, 0, n);
sem_init(&sQueue, 0, s);
sem_init(&eQueue, 0, e);
sem_init(&wQueue, 0, w);
nFirst = sFirst = eFirst = wFirst = 0;
pthread_t tid[i];
pthread_t checker;
pthread_create(&checker, NULL, checking, NULL);
for(int j = 0; j < i; j++)
{ pthread_create(&tid[j], NULL, bats, (void*) &j); }
for(int j = 0; j < i; j++)
{ pthread_join(tid[j], NULL); }
done = 1;
pthread_join(checker, NULL);
pthread_mutex_destroy(&thisMutex);
sem_destroy(&nQueue);
sem_destroy(&sQueue);
sem_destroy(&wQueue);
sem_destroy(&eQueue);
return 0;
}
void *checking(void *arg)
{
while( done == 0 )
{
if( nFirst == 1 || sFirst == 1 ||
eFirst == 1 || wFirst == 1 )
check();
}
exit(0);
}
void *bats(void *arg)
{
int index = *(int *)arg;
char *dir;
switch (directions[index])
{
case 'n':
dir = "North";
break;
case 's':
dir = "South";
break;
case 'e':
dir = "East";
break;
case'w':
dir = "West";
break;
}
arrive(index, dir);
leave(index, dir);
_exit(0);
}
/* Functions */
void arrive(int num, char *dir)
{
printf("BAT %d from %s arrives at crossing\n", num, dir);
if(strcmp(dir,"North")== 0)
{
sem_wait(&nQueue);
nFirst = 1;
while(eFirst == 1){ /* do nothing */ }
cross();
nFirst = 0;
sem_post(&nQueue);
}
else if(strcmp(dir,"West")== 0)
{
sem_wait(&wQueue);
wFirst = 1;
while(nFirst == 1){ /* do nothing */ }
cross();
wFirst = 0;
sem_post(&wQueue);
}
else if(strcmp(dir,"South")== 0)
{
sem_wait(&sQueue);
sFirst = 1;
while(wFirst == 1){ /* do nothing */ }
cross();
sFirst = 0;
sem_post(&sQueue);
}
else if(strcmp(dir,"East")== 0)
{
sem_wait(&eQueue);
eFirst = 1;
while(sFirst == 1){ /* do nothing */ }
cross();
eFirst= 0;
sem_post(&eQueue);
}
}
void cross()
{
pthread_mutex_lock(&thisMutex);
sleep(1);
pthread_mutex_unlock(&thisMutex);;
}
void leave(int num, char *dir)
{
printf("BAT %d from %s leaving crossing\n", num, dir);
}
void check()
{
if( nFirst == 1 && sFirst == 1 &&
eFirst == 1 && wFirst == 1 )
{
eFirst = 0;
sleep(1);
eFirst = 1;
}
}
The goal of this assignment is to
BATs arriving from the same direction line up behind the first BAT already at the crossing;
BATs arriving from the right always have the right of way (unless the waiting BAT receives a signal to go);
Deadlock has to be prevented
Starvation has to be prevented
Once you run the code, you'll see the last two lines have 'NULL' for the direction the BAT is arriving from. Need help to solve that issue
Thanks!
Your program has undefined behaviour becasue you are passing pointer to local variable into your thread function. In bats function when dereferencing pointer you may get garbage value:
for(int j = 0; j < i; j++)
{
pthread_create(&tid[j], NULL, bats, (void*) &j);
}
j is local inside for loop.
You could allocate memory to hold value for int
variable:
for(int j = 0; j < i; j++)
{
int* var = malloc(sizeof(int));
*var = j;
pthread_create(&tid[j], NULL, bats, var);
}
remember to free this memory in bats function:
void *bats(void *arg)
{
int index = *(int *)arg;
//...
leave(index, dir);
free (arg); // <--
pthread_exit(NULL); // instead of _exit(0);
}
dir is a char pointer. So you cannot directly use an equality sign like if( dir == "North") What you need is if ((strcmp(dir,"North")== 0)
in the loop below,
while( argv[1][i] == 'n' || argv[1][i] == 'e' ||
argv[1][i] == 's' || argv[1][i] == 'w' )
{
i++;
if(argv[1][i] == 'n'){n++;}
if(argv[1][i] == 's'){s++;}
if(argv[1][i] == 'e'){e++;}
if(argv[1][i] == 'w'){w++;}
}
You are incrementing i first inside the loop. So, for the last element in the array argv[1][i] you are reading junk values. The increment of i should be the last statement in the loop. i.e. after if(argv[1][i] == 'w'){w++;}
Related
I tried to make calculator supporting brackets, but
I have no idea how to deal if the user's input includes expression with spaces, for example:
input: (2 + 3) * 2
i got: 2
If it's normally get (2+3)*2, it counts 10
.
My code:
#include <stdio.h>
#define MAX_SIZE 1024
int insert_operand(int *operand, int * top_num, int num) /* data is pushed into the data stack*/
{
(*top_num) ++;
operand[*top_num] = num; /*save data*/
return 0; /*Exit normally*/
}
int insert_oper (char * oper , int *top_oper , char ch)
{
(*top_oper)++;
oper[*top_oper] = ch; /*save operator*/
return 0; /*Exit normally*/
}
int compare(char *oper , int *top_oper , char ch) /* compare the priority of the operating server*/
{
if((oper[*top_oper] == '-' || oper[*top_oper] == '+') /*Determine whether the current priority is higher than the priority of the operator at the top of the stack*/
&& (ch == '*' || ch == '/'))
{
return 0;
}
else if(*top_oper == -1 || ch == '('
|| (oper[*top_oper] == '(' && ch != ')')) /*Determine whether the operator stack is empty; whether the top operator is '('*/
{
return 0;
}
else if (oper[*top_oper] =='(' && ch == ')')
{
(*top_oper)--;
return 1;
}
else
{
return -1; /*Operate the operator*/
}
}
int deal_date(int *operand ,char *oper ,int *top_num, int *top_oper) /*perform data operation*/
{
int num_1 = operand[*top_num]; /*Take out two data from the data stack*/
int num_2 = operand[*top_num - 1];
int value = 0;
if(oper[*top_oper] == '+')
{
value = num_1 + num_2;
}
else if(oper[*top_oper] == '-')
{
value = num_2 - num_1;
}
else if(oper[*top_oper] == '*')
{
value = num_2 * num_1;
}
else if(oper[*top_oper] == '/')
{
value = num_2 / num_1;
}
(*top_num) --; /*Move the top of the data stack down one bit*/
operand[*top_num] = value; /*Push the obtained value into the data stack*/
(*top_oper) --; /*Move the top of the operator stack down one bit*/
}
int main()
{
int operand[MAX_SIZE] = {0}; /*data stack, initialize*/
int top_num = -1;
char oper[MAX_SIZE] = {0}; /*operator stack, initialize*/
int top_oper = -1;
char *str = (char *) malloc (sizeof(char) * 100); /*get expression (without =)*/
scanf("%s", str);
char* temp;
char dest[MAX_SIZE];
int num = 0;
int i = 0;
while(*str != '\0')
{
temp = dest;
while(*str >= '0' && *str <= '9') /*judging whether it is data*/
{
*temp = *str;
str++;
temp++;
} /*Encounter a symbol to exit*/
if(*str != '(' && *(temp - 1) != '\0') /*Determine whether the symbol is '('*/
{
*temp = '\0';
num = atoi(dest); /*convert string to number*/
insert_operand(operand, &top_num,num); /*Push data into the data stack*/
}
while(1)
{
i = compare(oper,&top_oper,*str); /*judgment operator priority*/
if(i == 0)
{
insert_oper(oper,&top_oper,*str); /*press operator*/
break;
}
else if(i == 1) /*judging whether the expression in brackets ends*/
{
str++;
}
else if(i == -1) /* data processing */
{
deal_date(operand,oper,&top_num,&top_oper);
}
}
`
str ++; /* point to the next character of the expression */
}
`
printf("%d\n",operand[0]); /*output result*/
return 0;
I tried to count the equation even if there is a space in it. Can someone please help?
Solving the problem by removing spaces:
So if you're working with equation as string you can simply remove spaces with function like this (there will be probably better way or function in library string.h but this was first guess):
char* DeleteSpaces( char* stringWithSpaces, size_t lengthOfString)
{
char* stringWithoutSpaces = (char*)calloc(lengthOfString + 1, sizeof(char));
if( !stringWithoutSpaces )
return NULL;
for( unsigned int x = 0, y = 0; x <= lengthOfString; x++, y++ )
{
if( stringWithSpaces[x] == ' ' ) // if the character is space
{
while( stringWithSpaces[x] == ' ' && x < lengthOfString ) // skip all the spaces OR go away before you hit '\0'
x++;
stringWithoutSpaces[y] = stringWithSpaces[x]; // then copy next character into new string
}
else // if there's no space just copy the character
stringWithoutSpaces[y] = stringWithSpaces[x];
}
return stringWithoutSpaces;
}
This will basically remove all spaces from your received equation. If you really need the smallest possible memory requirement you can use realloc() at the end of the function for more optimal memory usage.
Here's simple example of how to use the function so you can test it:
int main()
{
char firstString[] = "H e l l o w o r l d\0";
char* secondString;
secondString = DeleteSpaces( firstString, strlen(firstString) );
if( !secondString )
return -1;
printf( "%s", secondString );
free( secondString );
return 0;
}
Don't forget to use free(SecondString). I hope I helped you atleast a little :)
As with the previous answer, I added in a function to remove any spaces from the entered formula in order to process the requested calculation. Also, coupled with that, I revised the "scanf" input to read in all of the entered characters which looked to be another symptom you were facing. With that, following is a refactored version of your program with the additional space compression function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 1024
int insert_operand(int *operand, int * top_num, int num) /* data is pushed into the data stack*/
{
(*top_num) ++;
operand[*top_num] = num; /*save data*/
return 0; /*Exit normally*/
}
int insert_oper (char * oper , int *top_oper , char ch)
{
(*top_oper)++;
oper[*top_oper] = ch; /*save operator*/
return 0; /*Exit normally*/
}
int compare(char *oper , int *top_oper , char ch) /* compare the priority of the operating server*/
{
if((oper[*top_oper] == '-' || oper[*top_oper] == '+') /*Determine whether the current priority is higher than the priority of the operator at the top of the stack*/
&& (ch == '*' || ch == '/'))
{
return 0;
}
else if(*top_oper == -1 || ch == '('
|| (oper[*top_oper] == '(' && ch != ')')) /*Determine whether the operator stack is empty; whether the top operator is '('*/
{
return 0;
}
else if (oper[*top_oper] =='(' && ch == ')')
{
(*top_oper)--;
return 1;
}
else
{
return -1; /*Operate the operator*/
}
}
int deal_date(int *operand ,char *oper ,int *top_num, int *top_oper) /*perform data operation*/
{
int num_1 = operand[*top_num]; /*Take out two data from the data stack*/
int num_2 = operand[*top_num - 1];
int value = 0;
if(oper[*top_oper] == '+')
{
value = num_1 + num_2;
}
else if(oper[*top_oper] == '-')
{
value = num_2 - num_1;
}
else if(oper[*top_oper] == '*')
{
value = num_2 * num_1;
}
else if(oper[*top_oper] == '/')
{
value = num_2 / num_1;
}
(*top_num) --; /*Move the top of the data stack down one bit*/
operand[*top_num] = value; /*Push the obtained value into the data stack*/
(*top_oper) --; /*Move the top of the operator stack down one bit*/
return value;
}
void compress(char *stx) /* The additional function */
{
char work[101];
int i = strlen(stx);
strcpy(work, stx);
for (int j = 0; j < i; j++)
{
stx[j] = 0;
}
i = 0;
for (int j = 0; j < (int)strlen(work); j++)
{
if (work[j] != ' ')
{
stx[i] = work[j];
i++;
}
}
}
int main()
{
int operand[MAX_SIZE] = {0}; /*data stack, initialize*/
int top_num = -1;
char oper[MAX_SIZE] = {0}; /*operator stack, initialize*/
int top_oper = -1;
char *str = (char *) malloc (sizeof(char) * 100); /*get expression (without =)*/
//scanf("%s", str);
scanf("%[^\n]", str); /* Refined the scanf call to receive all characters prior to the newline/return character */
compress(str); /* Added this function to remove spaces */
char* temp;
char dest[MAX_SIZE];
int num = 0;
int i = 0;
while(*str != '\0')
{
temp = dest;
while(*str >= '0' && *str <= '9') /*judging whether it is data*/
{
*temp = *str;
str++;
temp++;
} /*Encounter a symbol to exit*/
if(*str != '(' && *(temp - 1) != '\0') /*Determine whether the symbol is '('*/
{
*temp = '\0';
num = atoi(dest); /*convert string to number*/
insert_operand(operand, &top_num,num); /*Push data into the data stack*/
}
while(1)
{
i = compare(oper,&top_oper,*str); /*judgment operator priority*/
if(i == 0)
{
insert_oper(oper,&top_oper,*str); /*press operator*/
break;
}
else if(i == 1) /*judging whether the expression in brackets ends*/
{
str++;
}
else if(i == -1) /* data processing */
{
deal_date(operand,oper,&top_num,&top_oper);
}
}
str ++; /* point to the next character of the expression */
}
printf("%d\n",operand[0]); /*output result*/
return 0;
}
Testing out your sample formula with some additional spacing to ensure the compression function was working properly, following was the terminal output.
#Vera:~/C_Programs/Console/Calculate/bin/Release$ ./Calculate
(2 + 3) * 2
10
Give that a try and see if it meets the spirit of your project.
As pointed out in the comments and other answers, the solution may be to simply "compact" the spaces out of the string before trying to analyse the string's content.
This doesn't require a lot of code:
#include <stdio.h>
char *stripSP( char *src ) {
for( char *d = src, *s = src; ( *d = *s ) != '\0'; s++ )
d += *d != ' ';
return src;
}
int main( void ) {
char *s[] = { "Hello", "H e l l ooo", "(2 + 5) * 3" };
for( int i = 0; i < 3; i++ ) {
printf( "From '%s' ", s[i] );
printf( "'%s'\n", stripSP( s[i] ) );
}
return 0;
}
From 'Hello' 'Hello'
From 'H e l l ooo' 'Hellooo'
From '(2 + 5) * 3' '(2+5)*3'
Even more compact would be to use array indexing:
char *stripSP( char s[] ) {
for( int f=0, t=0; (s[t] = s[f++]) != '\0'; t += s[t] != ' ' ) {}
return s;
}
I am trying to take a list of MAC Addresses and Device names from a text file and store them in an array called list;
struct list {
char ch;
char a[2], b[2], c[2], d[2], e[2], f[2], g[2], alias[32]; //chars a-g are supposed to be a maximum of two characters long to store the different segments of the mac addresses, and the alias stores the device name up to 32 characters.
};
The main function here, as of right now is supposed to open the file "Inet.txt" and read each character individually using "char cur = fgetc." The function then assigns the different parts of the MAC address to its corresponding position in chars a-g of the list struct, and the alias char if the function goes more than 2 chars without reaching a ":" or a " ". The length of the current char is represented by the variable k, which increases every time the program detects a letter or a number, and is reset to -1 every time variable 'cur' is assigned to something. There is also an array "struct list *head[32]; " which stores each line separately, the line number being identified by the variable "int i", which increases by one every time "cur == '\n'" starting at "int = 0." The main function is as follows;
int main()
{
FILE *fp;
char cur, temp[32], temp2[32], p;
struct list *head[32];
head[0]=(struct list*)malloc(sizeof(struct list));
int num = 0, d, data, devices, i = 0, j = -1, k = -1, l = 0;
char arr[100][2];
int count = 0;
//head = current = NULL;
fp = fopen("Inet.txt", "r");
if(fp==NULL)
{
printf("Error opening file.");
}
while((cur = fgetc(fp)) != EOF)
{
//stringchecker(cur)!=0
if((cur >= 48 && cur <= 57)||(cur >= 97 && cur <= 122)||(cur >= 65 && cur <= 90))
{
k++; //counter for temp array size
if(cur >= 97 && cur <= 122)
{
temp[k] = cur-32;
}
else
{
temp[k] = cur;
}
if(k>1)
{
strncpy(temp2, temp, k+1);
temp2[k+1] = '\0';
aloc(&head[i],temp2,7);
// k = -1;
}
}
else if(cur == ':')
{
if(count == 0)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 1)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 2)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 3)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 4)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 5)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
count++;
k = -1;
}
else if(cur == ' ')
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,6);
k = -1;
}
else if(cur == '\n')
{
printf("\n%s:%s:%s:%s:%s%s\nALIAS: %s", (*head[i]).a,(*head[i]).b,(*head[i]).c,(*head[i]).d,(*head[i]).e,(*head[i]).f,(*head[i]).alias);
exit(0);
devices++;
data++;
count = 0;
num = -1;
i++;
j = -1;
k = -1;
head[i]=(struct list*)malloc(sizeof(struct list));
//exit(0);
}
}
fclose(fp);
return 0;
}
The "aloc()" function assigns the current char up to 16 characters to a-g or alias depending on the value of the variable count, which is a parameter of this function. The aloc() function is as follows;
void aloc(struct list **head, char ch[16], int count) //assigns ch value to specific variable of the current head based on the value of count 1-7
{
if(count == 0)
{
strncpy((*head)->a,ch, 2);
}
else if(count == 1)
{
strncpy((*head)->b,ch, 2);
}
else if(count == 2)
{
strncpy((*head)->c,ch, 2);
}
else if(count == 3)
{
strncpy((*head)->d,ch, 2);
}
else if(count == 4)
{
strncpy((*head)->e,ch, 2);
}
else if(count == 5)
{
strncpy((*head)->f,ch, 2);
}
else if(count == 6)
{
strncpy((*head)->g,ch, 2);
}
else if(count == 7)
{
strncpy((*head)->alias,ch, 16);
}
}
The input text file "Inet.txt" is as follows;
A0:FB:C5:44:b8:45 PLATTE
58:24:29:0f:c8:ee JET
F1:C0:11:16:53:1F Wabash
A0:FB:C5:32:15:10 GREEN
33:68:29:a1:b2:3c Charlie
58:24:29:0A:0B:C0 BAKER
GG:01:X0:99:1A:45 FOXTROT
The main problem I am having with this code is the variables a-g are not being assigned correctly. When I run the program to only read the first line, I get the following output:
A0FBC544B8:FBC544B8:C544B8:44B8:B8
ALIAS: PLATTE%
When the output should be:
A0:FB:C5:44:B8
ALIAS: PLATTE
I am not sure which line is causing the entire mac address to be assigned to char a of the current list. I will post the code as I have it in its entirety here to avoid confusion.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct list {
char ch;
char a[2], b[2], c[2], d[2], e[2], f[2], g[2], alias[32];
};
void aloc(struct list **head, char ch[16], int count)
{
if(count == 0)
{
strncpy((*head)->a,ch, 2);
}
else if(count == 1)
{
strncpy((*head)->b,ch, 2);
}
else if(count == 2)
{
strncpy((*head)->c,ch, 2);
}
else if(count == 3)
{
strncpy((*head)->d,ch, 2);
}
else if(count == 4)
{
strncpy((*head)->e,ch, 2);
}
else if(count == 5)
{
strncpy((*head)->f,ch, 2);
}
else if(count == 6)
{
strncpy((*head)->g,ch, 2);
}
else if(count == 7)
{
strncpy((*head)->alias,ch, 16);
}
}
int main()
{
FILE *fp;
char cur, temp[32], temp2[32], p;
struct list *head[32];
head[0]=(struct list*)malloc(sizeof(struct list));
int num = 0, d, data, devices, i = 0, j = -1, k = -1, l = 0;
char arr[100][2];
int count = 0;
//head = current = NULL;
fp = fopen("Inet.txt", "r");
if(fp==NULL)
{
printf("Error opening file.");
}
while((cur = fgetc(fp)) != EOF)
{
//stringchecker(cur)!=0
if((cur >= 48 && cur <= 57)||(cur >= 97 && cur <= 122)||(cur >= 65 && cur <= 90))
{
k++; //counter for temp array size
if(cur >= 97 && cur <= 122)
{
temp[k] = cur-32;
}
else
{
temp[k] = cur;
}
if(k>1)
{
strncpy(temp2, temp, k+1);
temp2[k+1] = '\0';
aloc(&head[i],temp2,7);
// k = -1;
}
}
else if(cur == ':')
{
if(count == 0)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 1)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 2)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 3)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 4)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
else if(count == 5)
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,count);
}
count++;
k = -1;
}
else if(cur == ' ')
{
strncpy(temp2, temp, 2);
temp2[2] = '\0';
aloc(&head[i],temp2,6);
k = -1;
}
else if(cur == '\n')
{
printf("\n%s:%s:%s:%s:%s%s\nALIAS: %s", (*head[i]).a,(*head[i]).b,(*head[i]).c,(*head[i]).d,(*head[i]).e,(*head[i]).f,(*head[i]).alias);
exit(0);
devices++;
data++;
count = 0;
num = -1;
i++;
j = -1;
k = -1;
head[i]=(struct list*)malloc(sizeof(struct list));
//exit(0);
}
}
fclose(fp);
return 0;
}
I initially tried writing this program using linked lists, but I thought it would be easier to keep track of an array of list structs for use later in my program. However I keep getting the same problem with my output. Any help is appreciated.
If you remove exit(0); from the block here
else if(cur == '\n')
{
printf(/* ... */);
exit(0);
devices++;
data++;
count = 0;
/* ... */
then this program appears to work1.
I say "appears" because this program invokes Undefined Behaviour by printing non null-terminated buffers with the printf specifier %s.
You need to either specify a precision, being the maximum number of bytes to print, with each %s specifier. For example:
#include <stdio.h>
int main(void)
{
char buf[2] = "AB"; /* the null byte is not stored */
printf("%2s\n", buf);
}
Or, you need to ensure your buffers are large enough to store a desired string length plus the null-terminating byte. If you want to store a string of length 2, your buffer must be at least of size 3.
#include <stdio.h>
int main(void)
{
char buf[3] = "AB"; /* the null byte IS stored */
printf("%s\n", buf);
}
Note that strncpy is notoriously hard to use, as it does not null-terminate the buffer if the length of the source string is greater than or equal to the size provided.
1. You must also change char cur to int cur. On platforms when char is an unsigned type, you will not be able to reliably test against the negative int value of EOF. fgetc returns an int for this reason.
As pointed out in the comments, avoid magic numbers and instead use the functions found in <ctype.h>.
If your file contents are predictably formatted, you can just use fgets + sscanf to read each line. For example:
#include <stdio.h>
#include <stdlib.h>
#define MAX_ADDRS 256
struct address {
char a[3];
char b[3];
char c[3];
char d[3];
char e[3];
char f[3];
char alias[32];
};
size_t read_macs(struct address *addrs, size_t limit, FILE *f)
{
char buffer[512];
size_t n = 0;
while (n < limit && fgets(buffer, sizeof buffer, f)) {
int cv = sscanf(buffer, "%2s:%2s:%2s:%2s:%2s:%2s%31s",
addrs[n].a, addrs[n].b, addrs[n].c,
addrs[n].d, addrs[n].e, addrs[n].f,
addrs[n].alias);
if (7 == cv)
n++;
}
return n;
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: %s FILENAME\n", *argv);
return EXIT_FAILURE;
}
FILE *file = fopen(argv[1], "r");
if (!file) {
perror(argv[1]);
return EXIT_FAILURE;
}
struct address store[MAX_ADDRS];
size_t length = read_macs(store, MAX_ADDRS, file);
fclose(file);
for (size_t i = 0; i < length; i++)
printf("%s (%s:....:%s)\n",
store[i].alias, store[i].a, store[i].f);
}
$ ./a.out Inet.txt
PLATTE (A0:....:45)
JET (58:....:ee)
Wabash (F1:....:1F)
GREEN (A0:....:10)
Charlie (33:....:3c)
BAKER (58:....:C0)
FOXTROT (GG:....:45)
I am dealing with the hsearch function in my progam. I generate my key which is a char *. And the data I stored is an integer.
I always add an element without problem, but when I want to find with
ENTRY *elemp, elem;
elemp = hsearch(elem, FIND).
elemp->data is always a wrong value(not the one inserted).
Is there a known issue about that.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <search.h>
#define N 1
#define B 0
#define U 2
#define TAILLE 100000
char key[1000];
char* convTableToKeyString(int lignes, int colonnes, int joueur_tour, int tab[lignes][colonnes]) {
int indice = 0;
switch (joueur_tour) {
case 0:
key[indice++] = 'B';
break;
case 1:
key[indice++] = 'N';
break;
}
for (int i = 0; i < lignes; i++) {
for (int j = 0; j < colonnes; j++) {
switch (tab[i][j]) {
case 0:
key[indice++] = 'B';
break;
case 1:
key[indice++] = 'N';
break;
case 2:
key[indice++] = 'U';
break;
}
}
}
key[indice++] = '\0';
return key;
}
int valeur_configuration(int couleur_tour, int colonnes, int lignes, int tabEchec[lignes][colonnes]) {
ENTRY elem, *elemp;
int res;
int i,j;
int prochain_joueur = couleur_tour == B ? N : B;
int tabCase1[lignes][colonnes], tabCase2[lignes][colonnes], tabCase3[lignes][colonnes];
int lose_range = couleur_tour == B ? lignes-1 : 0;
int tabConf[3*colonnes];
int nbreCoupsPossibles = 0;
//generate the key and look if it already exist in the table
elem.key = convTableToKeyString(lignes, colonnes, couleur_tour, tabEchec);
elemp = hsearch(elem, FIND);
if (elemp != NULL) {
return (int) (elemp->data);
}
/* The value is not present in the hash, so I can calculate it */
//my code logic check the possible moves and add the value in the hash table
for (i = 0; i < lignes; i++) {
for (j = 0; j < colonnes; j++) {
int front = couleur_tour == B ? i-1 : i+1;
if (tabEchec[i][j] == couleur_tour) {
if (j != 0 && tabEchec[front][j-1] == prochain_joueur) {
/* some operations */
tabConf[nbreCoupsPossibles++] = valeur_configuration(prochain_joueur, colonnes, lignes, tabCase2);
if(tabConf[nbreCoupsPossibles-1] ==0) {
res = 1;
elem.data = (void *) res;
elemp = hsearch(elem, ENTER);
/* there should be no failures */
if (elemp == NULL) exit(EXIT_FAILURE);
return 1;
}
} // if (j != 0 && tabEchec[front][j-1] == prochain_joueur)
if (j != colonnes-1 && tabEchec[front][j+1] == prochain_joueur) {
/* some operations */
tabConf[nbreCoupsPossibles++] = valeur_configuration(prochain_joueur, colonnes, lignes, tabCase3);
if(tabConf[nbreCoupsPossibles-1] ==0) {
res = 1;
elem.data = (void *) res;
elemp = hsearch(elem, ENTER);
/* there should be no failures */
if (elemp == NULL) exit(EXIT_FAILURE);
return 1;
}
} // if (j != colonnes-1 && tabEchec[front][j+1] == prochain_joueur)
if (tabEchec[front][j] == U) {
/* some operations */
tabConf[nbreCoupsPossibles++] = valeur_configuration(prochain_joueur, colonnes, lignes, tabCase1);
if(tabConf[nbreCoupsPossibles-1] ==0) {
res = 1;
elem.data = (void *) res;
elemp = hsearch(elem, ENTER);
/* there should be no failures */
if (elemp == NULL) exit(EXIT_FAILURE);
return 1;
}
} // if (tabEchec[front][j].couleur == U)
} // if (tabEchec[i][j] == couleur_tour)
} // for (j = 0; j < colonnes; j++)
} // for (i = 0; i < lignes; i++)
if(nbreCoupsPossibles == 0) {
//Haven't move, I lost
res = 0;
elem.data = (void *) res;
elemp = hsearch(elem, ENTER);
/* there should be no failures */
if (elemp == NULL) exit(EXIT_FAILURE);
return 0;
}
i = 0;
int maxi = 0;
while (i < nbreCoupsPossibles && tabConf[i] > 0) {
maxi = maxi > tabConf[i] ? maxi : tabConf[i];
i++;
}
if (i >= nbreCoupsPossibles) {
res = -1 * (maxi+1);
elem.data = (void *) res;
elemp = hsearch(elem, ENTER);
/* there should be no failures */
if (elemp == NULL) exit(EXIT_FAILURE);
return res;
} else {
maxi = tabConf[i];
while (i < nbreCoupsPossibles) {
if (tabConf[i] < 0) {
maxi = maxi > tabConf[i] ? maxi : tabConf[i];
}
i++;
}
res = -1 * (maxi-1);
elem.data = (void *) res;
elemp = hsearch(elem, ENTER);
elemp = hsearch(elem, FIND);
/* there should be no failures */
if (elemp == NULL) exit(EXIT_FAILURE);
return res;
} // if (i >= nbreCoupsPossibles)
return 0;
}
/* Function call first call*/
int main() {
int n,m,i,j;
hcreate(TAILLE);
int colonnes;
int lignes;
char b = 'a';
scanf("%d", &n);
scanf("%d", &m);
int tabPions[n][m];
char temp[m];
//Fake : just for flushing scanf internal functions
scanf("%c", &b);
for (i = 0; i < n; i++) {
scanf("%[^\n]%*c", temp);
for (j = 0; j < m; j++) {
if (temp[j] != 'p' && temp[j] != 'P') tabPions[i][j] = U;
if (temp[j] == 'p') tabPions[i][j] = N;
if (temp[j] == 'P') tabPions[i][j] = B;
}
}
colonnes = m;
lignes = n;
int res = valeur_configuration(B, colonnes, lignes, tabPions);
printf("%d\n", res);
hdestroy();
}
I don't completely understand the program, but I think I can explain your hsearch problem.
You are reusing the global char key[1000] buffer for every key. hsearch doesn't allow that. When you add an item to the hash table, hsearch doesn't make a copy of the key string. It only copies the pointer.
Since you're using the same key buffer for all your ENTERs and your FINDs, every key in the hash table matches the one you're looking for, every time, because they're all the same string! Most of them are in the wrong hash bucket, making the result of a lookup somewhat random.
A quick fix would be to do elem.key = strdup(elem.key) just before each ENTER operation.
If you care about freeing the strings before your program exits, you'll have to work a little harder on the memory management. hsearch is not helpful in that area, since it doesn't provide an iterator.
I'm doing a shift-reduce algorithm for our compiler design subject. This is the code.
void shiftReduce(char str[MAX_CHAR], int prodNum, int line)
{
int limit = 5, y=0;
int substrFlag = 1; //0 true 1 false
int ctr,x, counter;
int match, next;
char stack[MAX_CHAR];
clearString(stack);
OUTER:while ((strcmp(stack, prod[0].left) != 0) && (y < limit))
{
addChar(stack, str[0]);
strcpy(str, dequeue(str));
printf("Stack = %s\nQueue = %s\n", stack, str);
for (ctr = 0; ctr < prodNum; ctr++)
{
if (strstr(stack, prod[ctr].right) != NULL)
{ //substring found
substrFlag = 0;
strcpy(stack, replace(stack, prod[ctr].right, prod[ctr].left));
goto OUTER;
}
}
if ((str[0] == '\n') || (str[0] == '\0'))
y++;
}
if (strcmp(stack, prod[0].left) == 0)
;//printf("%s - Accepted.\n", stack);
else
printf("Syntax error on line %i\n", line);
}
When I comment the printf("Stack = %s\nQueue = %s\n", stack, str); line, it works well. But when I uncomment it, it returns the code 3221225477.
BTW. This is the dequeue function:
char * dequeue (char str[MAX_CHAR])
{
int x = 0; char temp;
for (x = 0; x < length(str); x++)
{
if ((x+1) < length(str))
str[x] = str[x+1];
}
return str;
}
and the addChar function:
void addChar (char * str, char letter)
{
int a = 0;
while (str[a] != '\0')
a++;
str[a] = letter;
str[a+1] = '\0';
return;
}
and finally replace function.
char * replace (char orig[MAX_CHAR], char substr[MAX_CHAR], char rep[MAX_CHAR])
{
int match, end=0, next=0;
int flag = 0; //0 true 1 false
char temp [MAX_CHAR];
char store[MAX_CHAR];
if (strstr(orig, substr) == NULL)
return NULL;
int x,y;
for (x = 0; x < length(orig); x++)
{
if (orig[x] == substr[0]) //if current character is equal to first character of substring
{
match = x;
for (y = 0; y < length(substr); y++)
{
if (orig[match+y] != substr[y])
{
flag = 1;
break;
}
}
if (flag == 0)
{
next = match + length(substr);
for (y = 0; y < length(rep); y++)
{
temp[match+y] = rep[y];
end = (match+y);
}
for (y = next; y < length(orig); y++)
{
temp[y] = orig[next+(y-next)];
}
return temp;
}
}
else
{
addChar(temp, orig[x]);
}
}
return temp;
}
PS. The prod array:
struct RULES
{
char left[MAX_CHAR];
char right[MAX_CHAR];
} RULES;
struct RULES prod[MAX_RULES];
When I comment the printf("Stack = %s\nQueue = %s\n", stack, str); line, it works well. But when I uncomment it, it returns the code 3221225477.
Then most likely either stack or str has not been 0-terminated or points to invalid memory.
If I have a a data structure like a matrix or a tree, and I want to factor out a for loop from a very large function where the above variable is included, how should the call look like? I tried the following but I get a segmentation fault.
void write_command(int w, char *argv[], char *string[]) {
char *dest;
for (int r = 0; argv[r] != NULL; r++) {
dest = malloc(sizeof(char *) * strlen(argv[r]) + 1);
*dest = '0';
strcpy(dest, argv[r]);
string[w][r] = *dest;
free(dest);
}
}
I think you see what I'm trying to do but how should I declare the variables? I get segfault at string[w][r] = *dest;.
I don't think you want to see what I'm refactoring but it is the biggest and most unreadable function ever.
static int runCmd(const char *cmd) {
const char *cp;
pid_t pid;
int status;
struct command structcommand[15];
char **argv = 0;
int argc = 1;
bool pipe = false;
char *string[z][z];
char *pString3[40];
char *pString2[40];
int n = 0;
char **ptr1;
char string1[z];
bool keep = false;
char *pString1[z];
char *pString[z];
*pString1 = "\0";
*pString = "\0";
char *temp = {'\0'};
int w = 0;
bool b = false;
int j = 0;
int i;
int p = 0;
char **ptr;
char *tmpchar;
char *cmdtmp;
bool b1 = false;
char *dest;
int y = 0;
i = 0;
int h = 0;
nullterminate(string);
if (cmd) {
for (cp = cmd; *cp; cp++) {
if ((*cp >= 'a') && (*cp <= 'z')) {
continue;
}
if ((*cp >= 'A') && (*cp <= 'Z')) {
continue;
}
if (isDecimal(*cp)) {
continue;
}
if (isBlank(*cp)) {
continue;
}
if ((*cp == '.') || (*cp == '/') || (*cp == '-') ||
(*cp == '+') || (*cp == '=') || (*cp == '_') ||
(*cp == ':') || (*cp == ',') || (*cp == '\'') ||
(*cp == '"')) {
continue;
}
}
}
if (cmd) {
cmdtmp = malloc(sizeof(char *) * strlen(cmd) + 1);
strcpy(cmdtmp, cmd);
tmpchar = malloc(sizeof(char *) * strlen(cmd) + 1);
if (tmpchar == NULL) {
printf("Error allocating memory!\n"); /* print an error message */
return 1; /* return with failure */
}
strcpy(tmpchar, cmd);
ptr1 = str_split(pString3, cmdtmp, '|');
if (strstr(cmd, "|") == NULL) { /* not a pipeline */
makeArgs(cmd, &argc, (const char ***) &argv, pipe, 0, 0);
for (j = 0; j < argc; j++) {
string[0][j] = argv[j];
structcommand[i].argv = string[0]; /*process;*/
}
n++;
}
else {
for (i = 0; *(ptr1 + i); i++) { /* tokenize the input string for each pipeline*/
n++; /* save number of pipelines */
int e = 0; /* a counter */
*pString = "\0"; /* should malloc and free this? */
strcpy(string1, *(ptr1 + i));
if ((string1[0] != '\0') && !isspace(string1[0])) { /* this is neither the end nor a new argument */
ptr = str_split(pString2, *(&string1), ' '); /* split the string at the arguments */
h = 0;
for (j = 0; *(ptr + j); j++) { /* step through the arguments */
/* the pipeline is in cmdtmp and the argument/program is in ptr[i] */
if (ptr + j && !b && strstr(*(ptr + j), "'")) {
b = true;
strcpy(temp, *(ptr + j));
if (y < 1) {
y++;
}
}
while (b) {
if (*(ptr + j) && strstr(*(ptr + j), "'")) { /* end of quote */
b = false;
if (y < 1) {
string[i][j] = strcpy(temp, *(ptr + j));
}
y = 0;
}
else if (*(ptr + j)) { /* read until end of quote */
string[i][j] = temp;
continue;
} else {
b = false;
break;
}
}
if (ptr + j) {
if (*(ptr + j)[0] == '{') {
keep = true;
}
if (testFn(*(ptr + j))) { /* test for last char */
string[i][j - p] = concat(*pString1, *(ptr + j));
keep = false;
free(*pString1);
goto mylabel;
}
if (keep) {
*pString1 = concat(*pString1, *(ptr + j));
*pString1 = concat(*pString1, " ");
p++;
} else {
// strcpy(temp, *(ptr + j));
b1 = false;
int q = j;
for (e = 0; *(ptr + q + e); e++) { /* step through the string */
b1 = true;
if (*(ptr + e + q)) {
*pString = concat(*pString, *(ptr + e + q));
*pString = concat(*pString, " ");
}
j = e;
}
if (makeArgs(*pString, &argc, (const char ***) &argv, pipe, i, h)) {
write_command(&w, argv, string[w]);
/*for (int r = 0; argv[r] != NULL; r++) {
dest = malloc(sizeof(char *) * strlen(argv[r]) + 1);
*dest = '0';
strcpy(dest, argv[r]);
string[w][r] = dest;
}*/
w++;
} else {
if (!b1) { /* no args (?) */
for (int r = 0; argv[r] != NULL; r++) {
string[i][r] = argv[r];
}
}
}
}
}
}
mylabel:
free(ptr);
dump_argv((const char *) "d", argc, argv);
}
}
free(ptr1);
free(cmdtmp);
free(tmpchar);
}
for (i = 0; i < n; i++) {
for (j = 0; DEBUG && string[i][j] != NULL; j++) {
if (i == 0 && j == 0) printf("\n");
printf("p[%d][%d] %s\n", i, j, string[i][j]);
}
structcommand[i].argv = string[i];
}
fflush(NULL);
pid = fork();
if (pid < 0) {
perror("fork failed");
return -1;
}
/* If we are the child process, then go execute the string.*/
if (pid == 0) {
/* spawn(cmd);*/
fork_pipes(n, structcommand);
}
/*
* We are the parent process.
* Wait for the child to complete.
*/
status = 0;
while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR));
if (pid < 0) {
fprintf(stderr, "Error from waitpid: %s", strerror(errno));
return -1;
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "pid %ld: killed by signal %d\n",
(long) pid, WTERMSIG(status));
return -1;
}
}
return WEXITSTATUS(status);
}
I'm going to suppose that you are trying to make a deep copy of the argv array, with that being a NULL-terminated array of strings such as the second parameter of a C program's main() function. The function you present seems to assume that you have already allocated space for the destination array itself; its job seems limited to copying the argument strings.
First things first, then: let's look at the caller. If you're making a deep copy of a standard argument vector, then the type of the destination variable should be compatible with the type of argv itself (in the colloquial sense of "compatible"). If the lifetime of the copy does not need to extend past the host function's return, then a variable-length array would be a fine choice:
char *copy[argc + 1];
That relieves you of manually managing the memory of the array itself, but not of managing any memory uniquely allocated to its elements. On the other hand, if you need the copy to survive return from the function in which it is declared, then you'll have to use manual allocation:
char **copy = malloc((argc + 1) * sizeof(*copy));
if (!copy) /* handle allocation failure */ ;
Either way, you can pass the resulting array or pointer itself to your write_command() function, and the required parameter type is the same. It is pointless to pass a pointer to copy, because that function will not modify the pointer it receives as its argument; rather, it will modify the memory to which it points.
Here is the signature of the function you seem to want:
void write_command(char *argv[], char *string[]) {
Given such a signature, you would call it as ...
write_command(argv, copy);
....
The key step you seem to want to perform in the loop inside is
string[r] = strdup(argv[r]);
You can accomplish the same thing with a malloc(), initialize, strcpy() sequence, but that's a bit silly when stdrup() is ready-made for the same task. Do not forget to check its return value, however, (or in your original code, the return value of malloc()) in case memory allocation fails. Any way around, you must not free the allocated memory within write_command(), because that leaves you with invalid pointers inside your copied array.
Furthermore, even if you really do have a 2D array of char * in the caller, such as ...
char *copies[n][argc + 1];
... nothing changes with function write_command(). It doesn't need to know or care whether the array it's copying into is an element of a 2D array. You simply have to call it appropriately, something like:
write_command(argv, copies[w]);
No matter what, you must be sure to free the copied argument strings, but only after you no longer need them. Again, you cannot do that inside the write_command() function.