I'm new in programming. At current learning C language.I'm confused right now in a problem which is showing unexpected result after running.
The problem is-
I want to make a program which take input one more than when the user input 42 and stop taking input after it and print all the numbers user had typed before 42 has come.
For example-Input:
1
2
88
42
99
Output:
1
2
88
My Program-
#include <stdio.h>
int main()
{
int i = 1, j, temp;
printf("enter numbers:\n");
while (i)
{
scanf("%d", &temp);
i++;
int a[i];
a[i - 2] = temp;
if (temp == 42)
{
scanf("%d", &a[i - 1]);
for (j = 0 ; j < i - 1 ; j++)
{
if (a[j] == 42)
break;
else
printf("%d\n",a[j]);
}
}
if (temp == 42)
break;
}
return 0;
}
When i input the same value as in example i get unexpected result such as-
4196363
0
-1
Please help.And sorry for my bad english.Thankyou in advance.
You are printing uninitialized values because if i's value becomes 3 and greater values after that you never set a[0] as well as all the values before a[i - 2].
Thus causing Undefined Behavior at the following line
printf("%d\n", a[j]);
some values are uninitialized and you still try to print them.
You should notice that a is being redeclared at each iteration and you are not initializing the values because the values in the previous a where only valid in the previous iteration.
The a variable is valid only within the scope of the while loop but not only because it's not declared outside, also because it's deallocated when it becomes out of scope. Perhaps an example will make it clearer.
Suppose you had the following program
int main(void)
{
int *pointer = NULL;
if (pointer == NULL)
{
int array[5] = {1, 2, 3, 4, 5};
pointer = array;
}
/* `poitner' points to `array` but array was deallocated */
for (int i = 0 ; i < 5 ; ++i)
printf("%d\n", pointer[i]); /* This is UB */
return 0;
}
this would compile, however as you can see it will invokes undefined behavior.
Since it's not possible to understand what your code does, I suggest the following
#include <stdio.h>
int main()
{
int i = 1;
int a[100];
printf("enter numbers:\n");
while (i++ < 100)
{
if (scanf("%d%*c", &a[i - 2]) != 1)
return -1; /* this was invalid input, it will cause UB */
if (a[i - 2] == 42)
{
if (scanf("%d%*c", &a[i - 1]) != 1)
return -1; /* this was invalid input, it will cause UB */
for (int j = 0 ; j < i - 1 ; j++)
{
if (a[j] == 42)
break;
else
printf("%d\n", a[j]);
}
}
if (a[i - 2] == 42)
break;
}
return 0;
}
This at least removes undefiend behavior and meakes teh code more readable, but it's still not clear what is this program for or what it should do.
In general case because the number of entered values is unknown you need to allocate dynamically storage for a new entered value.
So I would build a list that contains entered values. The other approach as to use malloc and realloc for the whole array when a new value is entered. In my opinion it is a worse solution than to build a list.
The program can look the following way
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
struct node
{
int value;
struct node *next;
} * head = NULL;
struct node **current = &head;
int value;
int next = -1;
while ( next != 1 && scanf( "%d", &value ) == 1 )
{
if ( next == 0 || value == 42 ) ++next;
if ( next == -1 )
{
*current = malloc( sizeof( struct node ) );
( *current )->value = value;
( *current )->next = NULL;
current = &( *current )->next;
}
}
while ( head )
{
printf( "%d\n", head->value );
struct node *tmp = head;
head = head->next;
free( tmp );
}
}
If to enter for example the following values
1 2 3 4 5 6 7 8 9 42 10
then the program output will be
1
2
3
4
5
6
7
8
9
The list stores neither 42 nor the next value after 42 though you can modify the program such a way that it would store also these values.
As for your code then at least this statement
a[i - 2] = temp;
is wrong when i is equal to 1 and results in undefined behaviour of the program.
And I do not see that i would be changed.
I think your problem is trying to declare(and redeclare) a[i] multiple times with different values. The compiler needs to know the size of an array at compile time. So the compiler is setting aside memory for an integer array with one element and then the dynamic part of your code is attempting to redeclare it a run-time. Try moving a outside the while loop and declaring it with a well defined maximum number of integer values like:
#define MAX_NUMS 1000
int a[MAX_NUMS];
There are some other problems as listed above, but I tested this with GCC 4.9 compiler and it seems to give the desired result.
You are doing some unnecessary matter.
You have to check just this--
while(input_number)
{
if(input_number==42)
break;
else
printf("%d\n",input_number);
}
In my thought I had edited your code to store all number before 42 based on your code.
#include<stdio.h>
int main()
{
int i=1,j,temp;
int a[10010];
printf("enter numbers:\n");
while(i)
{
scanf("%d",&temp);
i++;
a[i-2]=temp;
if(temp==42)
{
for(j=0; j<i-2; j++)
{
if(a[j]==42)
break;
else
printf("%d\n",a[j]);
}
}
if (temp == 42)
break;
}
return 0;
}
Related
I want to write a function where I have a given array and number N. The last occurrence of this number I want to return address. If said number cannot be found I want to use a NULL-pointer
Start of the code I've made:
int main(void) {
int n = 3;
int ary[6] = { 1,3,7,8,3,9 };
for (int i = 0; i <= 6; i++) {
if (ary[i] == 3) {
printf("%u\n", ary[i]);
}
}
return 0;
}
result in command prompt:
3
3
The biggest trouble I'm having is:
it prints all occurrences, but not the last occurrence as I want
I haven't used pointers much, so I don't understand how to use the NULL-pointer
I see many minor problems in your program:
If you want to make a function, make a function so your parameters and return types are explicit, instead of coding directly in the main.
C arrays, like in most languages, start the indexing at 0 so if there are N element the first has index 0, then the second has 1, etc... So the very last element (the Nth) has index N-1, so in your for loops, always have condition "i < size", not "i <= size" or ( "i <= size-1" if y'r a weirdo)
If you want to act only on the last occurence of something, don't act on every. Just save every new occurence to the same variable and then, when you're sure it was the last, act on it.
A final version of the function you describe would be:
int* lastOccurence(int n, int* arr, int size){
int* pos = NULL;
for(int i = 0; i < size; i++){
if(arr[i] == n){
pos = &arr[i]; //Should be equal to arr + i*sizeof(int)
}
}
return pos;
}
int main(void){
int n = 3;
int ary[6] = { 1,3,7,8,3,9 };
printf("%p\n", lastOccurence(3, ary, 6);
return 0;
}
Then I'll add that the NULL pointer is just 0, I mean there is literally the line "#define NULL 0" inside the runtime headers. It is just a convention that the memory address 0 doesn't exist and we use NULL instead of 0 for clarity, but it's exactly the same.
Bugs:
i <= 6 accesses the array out of bounds, change to i < 6.
printf("%u\n", ary[i]); prints the value, not the index.
You don't actually compare the value against n but against a hard-coded 3.
I think that you are looking for something like this:
#include <stdio.h>
int main(void)
{
int n = 3;
int ary[6] = { 1,3,7,8,3,9 };
int* last_index = NULL;
for (int i = 0; i < 6; i++) {
if (ary[i] == n) {
last_index = &ary[i];
}
}
if(last_index == NULL) {
printf("Number not found\n");
}
else {
printf("Last index: %d\n", (int)(last_index - ary));
}
return 0;
}
The pointer last_index points at the last found item, if any. By subtracting the array's base address last_index - ary we do pointer arithmetic and get the array item.
The cast to int is necessary to avoid a quirk where subtracting pointers in C actually gives the result in a large integer type called ptrdiff_t - beginners need not worry about that one, so just cast.
First of all, you will read from out of array range, since your array last element is 5, and you read up to 6, which can lead in segmentation faults. #Ludin is right saying that you should change
for (int i = 0; i <= 6; i++) // reads from 0 to 6 range! It is roughly equal to for (int i = 0; i == 6; i++)
to:
for (int i = 0; i < 6; i++) // reads from 0 to 5
The last occurrence of this number I want to return as address.
You are printing only value of 3, not address. To do so, you need to use & operator.
If said number cannot be found I want to use a NULL-pointer
I don't understand, where do you want to return nullpointer? Main function can't return nullpointer, it is contradictory to its definition. To do so, you need to place it in separate function, and then return NULL.
If you want to return last occurence, then I would iterate from the end of this array:
for (int i = 5; i > -1; i--) {
if (ary[i] == 3) {
printf("place in array: %u\n", i); // to print iterator
printf("place in memory: %p\n", &(ary[i])); // to print pointer
break; // if you want to print only last occurence in array and don't read ruther
}
else if (i == 0) {
printf("None occurences found");
}
}
If you want to return an address you need yo use a function instead of writing code in main
As you want to return the address of the last occurence, you should iterate the array from last element towards the first element instead of iterating from first towards last elements.
Below are 2 different implementations of such a function.
#include <stdio.h>
#include <assert.h>
int* f(int n, size_t sz, int a[])
{
assert(sz > 0 && a != NULL);
// Iterate the array from last element towards first element
int* p = a + sz;
do
{
--p;
if (*p == n) return p;
} while(p != a);
return NULL;
}
int* g(int n, size_t sz, int a[])
{
assert(sz > 0 && a != NULL);
// Iterate the array from last element towards first element
size_t i = sz;
do
{
--i;
if (a[i] == n) return &a[i];
} while (i > 0);
return NULL;
}
int main(void)
{
int n = 3;
int ary[] = { 1,3,7,8,3,9 };
size_t elements = sizeof ary / sizeof ary[0];
int* p;
p = g(n, elements, ary); // or p = f(n, elements, ary);
if (p != NULL)
{
printf("Found at address %p - value %d\n", (void*)p, *p);
}
else
{
printf("Not found. The function returned %p\n", (void*)p);
}
return 0;
}
Working on the specified requirements in your question (i.e. a function that searches for the number and returns the address of its last occurrence, or NULL), the code below gives one way of fulfilling those. The comments included are intended to be self-explanatory.
#include <stdio.h>
// Note that an array, passed as an argument, is converted to a pointer (to the
// first element). We can change this in our function, because that pointer is
// passed BY VALUE (i.e. it's a copy), so it won't change the original
int* FindLast(int* arr, size_t length, int find)
{
int* answer = NULL; // The result pointer: set to NULL to start off with
for (size_t i = 0; i < length; ++i) { // Note the use of < rather than <=
if (*arr == find) {
answer = arr; // Found, so set our pointer to the ADDRESS of this element
// Note that, if multiple occurrences exist, the LAST one will be the answer
}
++arr; // Move on to the next element's address
}
return answer;
}
int main(void)
{
int num = 3; // Number to find
int ary[6] = { 1,3,7,8,3,9 }; // array to search
size_t arrlen = sizeof(ary) / sizeof(ary[0]); // Classic way to get length of an array
int* result = FindLast(ary, arrlen, num); // Call the function!
if (result == NULL) { // No match was found ...
printf("No match was found in the array!\n");
}
else {
printf("The address of the last match found is %p.\n", (void*)result); // Show the address
printf("The element at that address is: %d\n", *result); // Just for a verification/check!
}
return 0;
}
Lots of answers so far. All very good answers, too, so I won't repeat the same commentary about array bounds, etc.
I will, however, take a different approach and state, "I want to use a NULL-pointer" is a silly prerequisite for this task serving only to muddle and complicate a very simple problem. "I want to use ..." is chopping off your nose to spite your face.
The KISS principle is to "Keep It Simple, St....!!" Those who will read/modify your code will appreciate your efforts far more than admiring you for making wrong decisions that makes their day worse.
Arrays are easy to conceive of in terms of indexing to reach each element. If you want to train in the use of pointers and NULL pointers, I suggest you explore "linked lists" and/or "binary trees". Those data structures are founded on the utility of pointers.
int main( void ) {
const int n = 3, ary[] = { 1, 3, 7, 8, 3, 9 };
size_t sz = sizeof ary/sizeof ary[0];
// search for the LAST match by starting at the end, not the beginning.
while( sz-- )
if( ary[ sz ] == n ) {
printf( "ary[ %sz ] = %d\n", sz, n );
return 0;
}
puts( "not found" );
return 1; // failed to find it.
}
Consider that the array to be searched is many megabytes. To find the LAST match, it makes sense to start at the tail, not the head of the array.
Simple...
Invalid read and write of size 8 happening in modify_tab_size().
what am I doing wrong? Ive tried almost everything, I dont understand it.
// Function being tested.
int erase_repeated(int *nb_words, char **words) {
for (int i = 0; i < *nb_words; ++i) {
if (words[i] != 0) {
for (int b = 0; b < *nb_words; ++b) {
if (strcmp(words[i], words[b]) == 0 && b != i)
modify_tab_size(&b, nb_words, words);
}
}
}
return *nb_mots;
}
void modify_tab_size(int *b, int *nb_words_update, char **words) {
free(words[*b]);
for (int k = *b; k < *nb_words_update; k++) {
words[k] = words[k + 1]; <--------------------------read error
words[*nb_words_update + 1] = 0; <--------------------------write error
}
(*nb_words_update)--;
(*b)--;
}
The problem is k+1 and *nb_words_update + 1 can walk off the array, and it is. Add printf("k:%d, k+1:%d, *nb_words_update + 1: %d\n", k, k+1, *nb_words_update + 1); into the loop to see.
k:1, k+1:2, *nb_words_update + 1: 4
k:2, k+1:3, *nb_words_update + 1: 4
You've only allocated three slots, 3 and 4 walk off the end of the array.
Since nb_words_update starts as the length of the array, words[*nb_words_update + 1] = 0; is always going to be too large. words[*nb_words_update] = 0; is also too large.
What you seem to be trying to do is deleting an element from an array by shifting everything after it to the left.
void delete_element(char **words, int *b, int *size) {
// Free the string to be deleted.
free(words[*b]);
// Only go up to the second to last element to avoid walking off the array.
for (int i = *b; i < *size-1; i++) {
// Shift everything to the left.
words[i] = words[i+1];
}
// Null out the last element.
// Don't use 0 for NULL, it's confusing.
words[*size-1] = NULL;
// Decrement the size of the array.
(*size)--;
// Redo the check with the newly shifted element.
(*b)--;
}
This sort of thing is better done with a linked list.
Note that your code has a bug. The result is an array of two elements, but one of them is blank. In addition to the return value of erase_repeated, also test its side effect which is to modify words. Test that words contains what you think it does.
My task is to generate random numbers in range from 50 to 80, if generated number is even add it to list of even numbers, if it is odd add it to list of odd numbers. Program should run till both lists are filled with 10 numbers. Duplicates in list are not allowed. This code prints 20 different numbers and it is storing duplicates in list. So I think problem is with count_odd, count_even and checking for duplicates but I can’t find solution to that. Also I have to print all generated numbers and also both lists in the end. Numbers in linked list should be in descending order
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
typedef struct list numbers;
typedef struct list* Pos;
int PrintRandom();
Pos CreateNew();
int PrintList(Pos);
struct list {
int number;
Pos next;
};
int PrintList(Pos P) {
if (P == NULL)
printf("Empty list.\n");
while (P != NULL) {
printf("%d\n", P->number);
P = P->next;
}
return 0;
}
Pos CreateNew() {
Pos q = NULL;
q = (Pos)malloc(sizeof(struct list));
if (q == NULL) return NULL;
q->next = NULL;
return q;
}
int PrintRandom() {
int x = 0, max = 80, min = 50;
x = (rand() % (max - min + 1) + min);
printf("Random number is : %d\n", x);
return x;
}
int main() {
srand(time(0));
Pos even, odd, q = NULL;
even = malloc(sizeof(numbers));
odd = malloc(sizeof(numbers));
even->next = odd->next = NULL;
int count_even = 0, count_odd = 0, j;
Pos head_p =even, head_n=odd;
while (count_even < 10 & count_odd < 10) {
j = PrintRandom();
if (j % 2 == 0) {
q = CreateNew();
if (q == NULL) return -1;
q->number = j;
while (even->next != NULL && even->next->number > q->number) {
even = even->next;
}
if (even->number== q->number)
free(q);
else
q->next = even->next;
even->next = q;
even =head_p;
count_even++;
}
else {
q = CreateNew();
if (q == NULL) return -1;
q->number = j;
while (odd->next != NULL && odd->next->number > q->number) {
odd = odd->next;
}
if (odd->number == q->number)
free(q);
else
q->next = odd->next;
odd->next = q;
odd = head_n;
count_odd++;
}
}
printf("Even numbers in list are:\n");
PrintList(head_p->next);
printf("Odd numbers in list are:n");
PrintList(head_n->next);
return 0;
}
Your code has several bugs:
First bug:
The loop
while (count_even < 10 & count_odd < 10) {
will stop as soon as either count_even or count_odd reaches 10. However, in your question, you stated that the loop should only stop when both have reached 10.
Therefore, you should change that line to the following:
while ( count_even < 10 || count_odd < 10 ) {
Also, it is worth pointing out that & is the bitwise-AND operator. You probably intended to use the logical-AND operator, which is &&.
Second bug:
In the comments section, you stated that if one of the lists is already full (has 10 elements), then all additional numbers that belong in that list should be discarded instead. However, your program does not contain any code to check for this. Instead, your program will keep adding new elements to the list, even if it already has 10 elements.
Third bug:
The following code is wrong:
if (even->number== q->number)
free(q);
else
q->next = even->next;
even->next = q;
even =head_p;
count_even++;
First of all, you should change the expression
even->number== q->number
to:
even->next->number== q->number
However, since even->next may be null, you must test for that beforehand, so that the entire line would look like this:
if ( even->next != NULL && even->next->number== q->number)
Also, the lines
even->next = q;
and
count_even++;
should not be executed if the number already exists. Therefore, you should move these lines inside the else block. After performing these changes, your code should look like this:
if ( even->next != NULL && even->next->number== q->number)
{
free(q);
}
else
{
q->next = even->next;
even->next = q;
count_even++;
}
even =head_p;
The same changes should be performed on the code branch for the odd numbers.
Fourth Bug:
There is a backslash character missing in the following line:
printf("Odd numbers in list are:n");
You should change it to:
printf("Odd numbers in list are:\n");
Additional remarks:
There are two other things that I think could be improved in your code:
The first element of the linked list is just a dummy node in which the number field is not initialized. You seem to be using this node for nothing else than holding the head pointer to the actual linked list.
You have a lot of code duplication. Instead of using the same code for handling both even and odd numbers, you have separate, but very similar code for handling each case.
In the code below, I have rewritten most of your code to show you can see how I would solve this problem. Don't be surprised if you find it hard to understand, as I am using pointers to pointers, which can be hard for beginners to understand. As you can see, I am not using any dummy nodes, and I am also using the same code for handling both even and odd numbers. Only in two places of my code do I have different code for handling even and odd numbers.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
struct list_node
{
int number;
struct list_node *next;
};
int PrintRandom();
int PrintList( struct list_node * );
int PrintList( struct list_node *p)
{
if ( p == NULL )
{
printf( "Empty list.\n" );
}
else
{
while ( p != NULL )
{
printf( "%d\n", p->number );
p = p->next;
}
}
return 0;
}
int PrintRandom() {
int x = 0, max = 80, min = 50;
x = (rand() % (max - min + 1) + min);
printf("Random number is : %d\n", x);
return x;
}
int main()
{
srand( (unsigned)time(NULL) );
struct list_node *head_even = NULL, *head_odd = NULL;
int count_even = 0, count_odd = 0, random;
bool is_even;
while ( count_even < 10 || count_odd < 10 )
{
struct list_node **pp, *p, *q;
//generate and print random number
random = PrintRandom();
//determine whether number is even or odd
is_even = random % 2 == 0;
//set pp to point to head pointer of the appropriate list
//and check whether the appropriate list is already full
if ( is_even )
{
if ( count_even >= 10 )
continue;
pp = &head_even;
}
else
{
if ( count_odd >= 10 )
continue;
pp = &head_odd;
}
for (;;) //infinite loop, equivalent to while (1)
{
p = *pp;
//if we reached the end of the list, break out of the loop
if ( p == NULL )
break;
if ( p->number <= random )
{
if ( p->number == random )
{
//discard number, because it already exists in list
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto next_outer_loop_iteration;
}
else
{
break;
}
}
pp = &p->next;
}
//allocate memory for new node
q = malloc( sizeof *q );
if ( q == NULL)
{
fprintf( stderr, "error allocating memory for new node!\n" );
exit( EXIT_FAILURE );
}
//initialize new node
q->number = random;
q->next = p;
//insert node into list
*pp = q;
//increment the appropriate counter
if ( is_even )
count_even++;
else
count_odd++;
next_outer_loop_iteration:
continue;
}
printf("Even numbers in list are:\n");
PrintList( head_even );
printf("Odd numbers in list are:\n");
PrintList( head_odd );
//NOTE: The memory of the linked lists is not being freed
return 0;
}
I am trying to write a code that will simulate a circle of a fixed size of people that have one sword. The closest "living person" to the current index will be eliminated and the sword will be passed to the next living person (after the one who got killed) and so on.
I want it to be written without linked lists.
Example:
A group of 3 people:
arr[0] = 1, arr[1] = 1, arr[2] = 1
First turn:
arr[0] KILLS arr[1] and the sword gets PASSED to arr[2]
Values of elements after the first turn:
arr[0] = 1, arr[1] = 0, arr[2] = 1
Second turn:
arr[2] KILLS arr[0] and stays the last player
Values of elements after the second turn:
arr[0] = 0, arr[1] = 0, arr[2] = 1
arr[2]'s index gets returned by the main function.
What I thought about was:
array
set the values all of elements to 1
circulate and check each time if (1 == arr[i])
set a flag to determine whether to kill or just pass the sword to this guy.
return the current index that indicates that's the index of the last player alive.
For example, lets say we have 5 people in our group:
[1] [1] [1] [1] [1]
First round:
give_sword = 0
i = 0 does not enter the first if because give_sword is not 1.
It enters the second if, and finds the closest living person using the function findClosestLivingPerson and gets his index and sets his value to 0 (== kills the closest living person).
It sets give_sword to 1.
Decreases the players_counter and checks if there is only one player left. If not, continues the loop.
This is my code:
#include <stdio.h>
int findClosestLivingPerson(int arr[], int index, int group_size);
int main (int argc, char *argv[])
{
int group_size = 0, players_counter = 0;
int i = 0, give_sword = 0;
int arr[100] = {0};
printf("Enter group size: \n");
scanf("%d",&group_size);
for (i = 0; i < group_size; i++)
{
arr[i] = 1;
}
players_counter = group_size;
for (i = 0; i < group_size; (i+1) % group_size)
{
if (1 == arr[i])
{
if(1 == give_sword) /* should give sword, not to kill */
{
give_sword = 0;
}
else /* should be killed */
{
arr[findClosestLivingPerson(arr,i, group_size)] = 0;
give_sword = 1;
--players_counter;
if (players_counter == 1)
{
break;
}
}
}
}
printf("Winner is %d ",i);
return 0;
}
int findClosestLivingPerson(int arr[], int index, int group_size)
{
for (; index < group_size; (index+1) % group_size)
{
if (arr[index] == 1)
return index;
}
return 0;
}
The compiler says:
In function ‘main’: last_man.c:23:43: warning: statement with no
effect [-Wunused-value] 23 | for (i = 0; i < group_size;
(i+1) % group_size)
last_man.c: In function ‘findClosestLivingPerson’: last_man.c:49:42:
warning: statement with no effect [-Wunused-value] 49 | for (;
index < group_size; (index+1) % group_size)
The (index+1) % group_size is meant to circulate through this array.
As the compiler says, (i+1) % group_size has no effect. It computes the remainder of the sum of i and one. After that, it does nothing with the result.
The third part of a for statement is just an expression that is evaluated. It does not automatically update the loop index or do anything else. If you want it to update i, you must write an assignment, like i = (i+1) % group_size.
I think you've misunderstood how for loop works .
Format should be like this:
for( initialization, condition, iteration )
example:
for( int i = 0; i < size; i = i + 1 )
(i + 1) % group_size isn't an iteration( it isn't assigning the result to i ) , what you really want to do is
i = ( i + 1 ) % group_size;
Same applies for the second warning .
I would suggest a different approach. Let's do this in a way that results in nice code.
struct actor {
int label;
struct actor *next;
};
With this struct we can make a nice linked list and loop it back around:
int n = 5;
int i;
struct actor *actors = calloc(n, sizeof *actors);
for (i = 0; i < n - 1; i++) {
actors[i].label = i;
actors[i].next = &actors[i+1];
}
actors[i].label = i;
actors[i].next = &actors[0];
Ok, now we can assign the first killer:
struct actor *k = actors;
We also need a kill function:
struct actor *kill(struct actor *a)
{
if (a->next != a) {
printf("%d kills %d\n", a->label, a->next->label);
a->next = a->next->next;
} else {
printf("%d is last man standing\n", a->label);
a->next = NULL;
}
return a->next;
}
What this does: it removes the next person from the circular linked list (as that is the one that is killed). The lookup time for the next person is always the same.
And with everything in place, we can start the spree:
while (k) {
k = kill(k);
}
This is not perfect code by any means, but it is a nice example of how you can make the algorithm incredibly simple if you put a little effort in setting up.
I am new to C so I am having troubles with making a hash table and malloc-ing spaces.
I am doing an anagram solver. Right now I am still at the step where I create the hash table for this program. I am trying to test my insert function to see if it is working properly by calling the function once with some random arguments.
However, I kept getting segmentation faults, and I used valgrind to track down where it crashes.
Can you point out what am I missing?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int hash(char *word)
{
int h = 0;
int i, j;
char *A;
char *a;
// an array of 26 slots for 26 uppercase letters in the alphabet
A = (char *)malloc(26 * sizeof(char));
// an array of 26 slots for 26 lowercase letters in the alphabet
a = (char *)malloc(26 * sizeof(char));
for (i = 0; i < 26; i++) {
A[i] = (char)(i + 65); // fill the array from A to Z
a[i] = (char)(i + 97); // fill the array from a to z
}
for (i = 0; i < strlen(word); i++) {
for (j = 0; j < 26; j++) {
// upper and lower case have the same hash value
if (word[i] == A[j] || word[i] == a[j]) {
h += j; // get the hash value of the word
break;
}
}
}
return h;
}
typedef struct Entry {
char *word;
int len;
struct Entry *next;
} Entry;
#define TABLE_SIZE 20 // test number
Entry *table[TABLE_SIZE] = { NULL };
void init() {
// create memory spaces for each element
struct Entry *en = (struct Entry *)malloc(sizeof(struct Entry));
int i;
// initialize
for (i = 0; i < TABLE_SIZE; i++) {
en->word = "";
en->len = 0;
en->next = table[i];
table[i] = en;
}
}
void insertElement(char *word, int len) {
int h = hash(word);
int i = 0;
// check if value has already existed
while(i < TABLE_SIZE && (strcmp(table[h]->word, "") != 0)) {
// !!!! NEXT LINE IS WHERE IT CRASHES !!!
if (strcmp(table[h]->word, word) == 0) { // found
table[h]->len = len;
return; // exit function and skip the rest
}
i++; // increment loop index
}
// found empty element
if (strcmp(table[h]->word, "") == 0) {
struct Entry *en;
en->word = word;
en->len = len;
en->next = table[h];
table[h] = en;
}
}
int main() {
init(); // initialize hash table
// test call
insertElement("kkj\0", 2);
int i;
for ( i=0; i < 10; i++)
{
printf("%d: ", i);
struct Entry *enTemp = table[i];
while (enTemp->next != NULL)
{
printf("Word: %s, Len:%d)", enTemp->word, enTemp->len);
enTemp = enTemp->next;
}
printf("\n");
}
return 0;
}
It's not necessary to cast the return value from malloc, and doing so can mask other errors.
The following lines malloc memory which is never freed, so there's a memory leak in your hash function.
// an array of 26 slots for 26 uppercase letters in the alphabet
A = (char *)malloc(26 * sizeof(char));
// an array of 26 slots for 26 lowercase letters in the alphabet
a = (char *)malloc(26 * sizeof(char));
sizeof(char) is guaranteed to be 1, by definition, so it's not necessary to multiply by sizeof(char).
Your code also assume ascii layout of the characters, which is not guaranteed.
In the init() function, you have
// create memory spaces for each element
struct Entry *en = (struct Entry *)malloc(sizeof(struct Entry));
does not do what the comment says. It only allocates enough memory for one struct Entry. Perhaps you meant to put this inside the loop.
For a fixed table size you could also just have an array of struct Entry
directly rather than an array of pointers to such. I.e.
struct Entry table[TABLE_SIZE] = { 0 };
And then you wouldn't need to malloc memory for the entries themselves, just the contents.
In your initialization loop
for (i = 0; i < TABLE_SIZE; i++) {
en->word = "";
en->len = 0;
en->next = table[i];
table[i] = en;
}
each en->next is set to itself, and all of the table elements are set to the same value. The first time through the loop, en->next is set to table[0], which at this point is NULL due to your static initializer. table[0] is then set to en.
The second time through the loop, en->next is set to table[1], which is also null. And en hasn't changed, it is still pointing to the result from the earlier malloc. table[1] is then set to en, which is the same value you had before. So, when you're done, every element of table is set to the same value, and en->next is NULL.
I haven't traced through the hash function, but I don't immediately see
anything limiting the use of the hash value to possible indexes of table. When I tested it, "kkj\0" (btw, String literals in C are already null terminated, so the \0 isn't needed.) had a hash value of 29, which is outside the valid
indexes of table. So you are accessing memory outside the limits of the table
array. At that point all bets are off and pretty much anything can happen. A
seg fault in this case is actually a good result, since it's immediately
obvious something's wrong. You need to take the hash value modulo the table
size to fix the array bounds issue, i.e. h % TABLE_SIZE.