I have written a program which could sort 5 strings you inputed from small to big. However, it can't work. I have worked at it for almost an hour, but I couldn't find out the problem. Here is the code.
#include <stdio.h>
#include <string.h>
main() {
char *sz[5], *temp;
int i, j;
for(i = 0; i < 5; i++) {
gets(sz[i]);
fflush(stdin);
}
for(i = 0; i < 5; i++) {
for(j = i+1; j < 5; j++) {
if(strcmp(sz[i], sz[j]) > 0) {
temp = sz[i];
sz[i] = sz[j];
sz[j] = temp;
}
}
puts(sz[i]);
puts("");
}
}
First huge problem is that you are using a routine that should never have existed, and using it improperly:
char *sz[5], *temp;
int i, j;
for(i = 0; i < 5; i++) {
gets(sz[i]);
You did not allocate any storage for gets() to store into, so it is simply scribbling on unrelated memory. (This often leads to security problems.)
You should pay special attention to the BUGS section in your manpages:
BUGS
Never use gets(). Because it is impossible to tell without
knowing the data in advance how many characters gets() will
read, and because gets() will continue to store characters
past the end of the buffer, it is extremely dangerous to use.
It has been used to break computer security. Use fgets()
instead.
Unlearn gets(3), now, and be a happier programmer.
Use malloc() to allocate some memory for those character arrays.
Ignacio hit another problem squarely on the head -- you're printing before your sort is finished. Add another loop to print, after the sorting. (Better yet, put the input, sort, and output into three separate functions. Perhaps you're not there yet, but it would be well worth doing this sooner rather than later, as it makes testing your programs significantly easier to have printing functions you can use for debugging.)
char *sz[5], *temp;
int i, j;
for(i = 0; i < 5; i++) {
gets(sz[i]); /* Tries to write data to random location. */
fflush(stdin);
}
At least 3 problems:
You're writing to uninitialized pointers. You need to initialize sz[i] before using it (perhaps using malloc)
fflush(stdin) is undefined behavior, drop it
gets is unsafe and was removed from the standard, drop it as well and use fgets instead
You are passing uninitialized pointers to gets, storing the data in random locations. This is undefined behavior. You should allocate memory for your data, and use fgets with limits to read strings.
char *sz[5], *temp;
int i, j;
char buf[100];
for(i = 0; i < 5; i++) {
fgets (buf , 100 , stdin);
sz[i] = strdup(buf);
}
... sort your strings...
// Free the strings before exiting the program
for (i = 0 ; i < 5 ; i++) free(sz[i]);
Related
I am trying to implement Insertion sort algorithm in C.
But all I get is SIGSEGV error in online IDEs and the output doesn't show up in Code::Blocks. How to avoid Such errors.
#include <stdio.h>
#include <stdlib.h>
int main()
{
/* Here i and j are for loop counters, temp for swapping
count for total number of elements,array for elements*/
int i, j, temp, count;
printf("How many numbers are you going to enter");
scanf("%d", &count);
int n[20];
printf("Enter %d elements", count);
// storing elements in the array
for(i = 0; i < count; i++) {
scanf("%d", n[i]);
}
// Implementation of insertion sort algorithm
for(i = 0; i < count; i++) {
temp = n[i];
j = i - 1;
while(temp < n[j]) {
n[j+1] = n[j];
j = j - 1;
}
n[j+1] = temp;
}
printf("Order of sorted elements");
for(i = 0; i < count; i++) {
printf("%d", n[i]);
}
return 0;
}
There are a couple of problems with your code. First of all, what is a SIGSEGV error? Well, it's another name for the good old Segmentation fault error, which is basically the error you get when accessing invalid memory (that is, memory you are not allowed to access).
tl;dr: change scanf("%d",n[i]); to scanf("%d",&n[i]);. You're trying to read the initial values with scanf("%d",n[i]);, this raises a segmentation fault error because scanf expects addresses in which put the values read, but what you're really doing is passing the value of n[i] as if it were an address (which it's not, because, as you did not set any value for it yet, it's pretty much just memory garbage). More on that here.
tl;dr: change int n[20]; to int n[count]. Your array declaration int n[20]; is going to store at most 20 integers, what happens if someone wants to insert 21 or more values? Your program reserved a certain stack (memory) space, if you exceed that space, then you're going to stumble upon another program's space and the police (kernel) will arrest you (segmentation fault). Hint: try inserting 21 and then 100 values and see what happens.
tl;dr: change for(i = 0; i < count; i++) { to for(i = 1; i <= count; i++) {. This one is a logic problem with your indexes, you are starting at i = 0 and going until i = count - 1 which would be correct in most array iteration cases, but as j assumes values of indexes before i, you need i to start from 1 (so j is 0, otherwise j = -1 in the first iteration (not a valid index)).
My final code is as follows. Hope it helped, happy coding!
#include <stdio.h>
#include <stdlib.h>
int main() {
/*Here i and j are for loop counters,temp for swapping
count for total number of elements,array for elements*/
int i, j, temp, count;
printf("How many numbers are you going to enter?\n");
scanf("%d",&count);
int n[count];
printf("Enter %d elements\n",count);
//storing elements in the array
for(i = 0; i < count; i++) {
scanf("%d", &n[i]);
}
//Implementation of insertion sort algorithm
for(i = 1; i <= count; i++) {
temp = n[i];
j = i-1;
while(temp < n[j]) {
n[j+1] = n[j];
j--;
}
n[j+1] = temp;
}
printf("Order of sorted elements\n");
for(i = 0; i < count; i++) {
printf("%d\n",n[i]);
}
return 0;
}
Edit: If you're having trouble with online IDEs, consider running your programs locally, it saves a lot of time, plus: you never know what kernel version or magic the online IDEs are using to run your code (trust me, when you're coding in C -- fairly low level language, these things make a difference sometimes). I like to go all root style using Vim as text editor and gcc for compiling as well as gdb for debugging.
EDIT: Question answered, I had a typo in my first for loop. Thanks so much everyone for catching that.
I'm working a C project for uni, and I'm having a problem I just cannot figure out. In one section of the program, I have to read in a few strings of symbols into a 2D character array in a struct (aka a "stamp"). Here is the code in my function:
stamp_t * read_stamp_type1(FILE * fptr)
{
//variable declaration
int r = 0, c = 0;
//creating a struct stamp_t and mallocing memory
stamp_t *newstamp1;
newstamp1 = malloc(sizeof(stamp_t));
//reading in values for the rows and columns of the "stamp" to be read in
fscanf(fptr, "%d %d\n", &r, &c);
//storing these values
newstamp1->num_rows = r;
newstamp1->num_cols = c;
//creating memory for newstamp1's grid
newstamp1->grid = malloc(sizeof(char *) * (r));
for(int i=0; i < c; i++)
newstamp1->grid[i] = malloc(sizeof(char) * (c+1));
//string to temporarily store input
char rowvalues[c+1];
//Note: Everything works up until this point
//the below lines crash the program every time
for(int i = 0; i < r; i++)
{
fscanf(fptr, "%s", rowvalues);
for (int j=0; j < c; j++)
strcpy(newstamp1->grid[i], rowvalues);
}
free(rowvalues);
return(newstamp1);
}
For some reason when I try to fscanf the string from the text file, it crashes the program (or at least that's what I think the cause is...).
For reference, here is the struct declaration:
// A structure for holding a stamp
typedef struct
{
// The size of the contents of this stamp.
int num_rows;
int num_cols;
// A 2D array of characters for a stamp.
char **grid;
} stamp_t;
And here is the input to the program:
3 4
.#.#
#.#.
.#.#
Any advice would be greatly appreciated, I can't seem to find any problem with the code. I have tried manually assigning values to each value in the rowvalues array, which works fine (rowvalues[0] = 'c'; works fine). I need to read in the 3 lines of symbols into newstamp1.grid, which is a 2D array. I ran a debugger and it said it's try to write to memory that is not allowed ("Access violation writing location"). I emailed my professor but he hasn't be in class for the past week and he isn't responding to email...
Thanks so much in advance.
For starters there is a typo in this loop
newstamp1->grid = malloc(sizeof(char *) * (r));
for(int i=0; i < c; i++)
^^^^^^
newstamp1->grid[i] = malloc(sizeof(char) * (c+1));
There must be
for(int i=0; i < r; i++)
^^^^^^
This loop
for (int j=0; j < c; j++)
strcpy(newstamp1->grid[i], rowvalues);
does not make sense. It seems you mean just
strcpy(newstamp1->grid[i], rowvalues);
Or maybe you have to allocate a 3D character array (that is a two dimensional array of strings) if each row in the file contains c strings.
And this statement
free(rowvalues);
is wrong. The variable rowvalues has automatic storage duration. So you may not call the function free for it.
This part of your code is nonsense:
newstamp1->grid = malloc(sizeof(char *) * (r));
for(int i=0; i < c; i++)
newstamp1->grid[i] = malloc(sizeof(char) * (c+1));
You allocate r items, and then your for is repeated as c-times. This should be the same, either both r, or both c. I bet i < r is correct condition there.
Also, as Vlad from Moscow pointed out, there is one more bug in the code: The final free shouldn't be there at all, because it wasn't mallocated. And the for-j line can also be omitted. (But it shouldn't produce any errors, it just unneededly repeats the same code.)
I wrote a small program to combine two 2d arrays. Here is the code:
#define MAX 7
int main(void) {
int i, j;
char *array1[] = {"Welt,", "bist", "du"};
char *array2[] = {"noch", "zu", "retten?"};
char final[MAX][MAX];
for(i = 0; i < 3; i++) {
// initialize ith names element with first name
strcpy(final[i], array1[i]);
}
for(j = 0; j < 3; j++) {
// concatenate the last name to the firstname+space string
strcat(final[i], array2[j]);
}
for (i = 0; i != 6; i++) {
printf("%s", final[i]);
}
return EXIT_SUCCESS;
}
I get really strange output like:
Welt,bistbistdunochzuretten?uretten?en?
while what I want is this:
Welt,bistdunochzuretten
As you can see it is not completely wrong. There should not be a space between the words.
How can I fix my code?
The problems were that in the second for you were doing strcat(final[3], array2[j]);, because i was 3 at that point and in the final for you were trying to print from final[0] to final[5], when you only had defined final[0] to final[3] (where on final[0] to final[2] you had the names, and in final[3] you had all the last names concatenated which also exceeded the limit of characters), and without printing them in a new line it was hard to tell which string was what.
Try this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 7
int main(void) {
int i,j;
char *array1[] = {"Welt","bist","du"};
char *array2[] = {"noch","zu","retten?"};
char final[MAX][MAX];
for(i=0;i<3;i++)
strcpy(final[i], array1[i]); //To initialize ith names element with first name
for(j=0;j<3;j++)
strcat(final[j],array2[j]); //Concatanate the last name to the firstname+ space string
for (i = 0; i < 3; i++)
printf("%s\n", final[i]);
return EXIT_SUCCESS;
}
There are several problems with your code:
The constant MAX is not large enough for your data. The string "retten?" contains seven characters plus one terminating byte. As such, MAX must be at least 8, otherwise you get undefined behavior.
Your second loop contains uses the wrong index into final[i]. See point 3. for corrected versions.
The use of strcat() is wrong, you should be using strcpy() just like in the first loop. Together with point 2., your second loop should either look like this:
for(j = 0; j < 3; i++, j++) { //add increment for i
strcpy(final[i], array2[j]);
}
or like this:
for(j = 0; j < 3; j++) {
strcpy(final[3 + j], array2[j]); //derive the index from j
}
Regarding Point 1, I always advise against using any compile time constants like MAX. My experience is that these are just bugs that are waiting to strike. Someday, someone will have a use case that exceeds the limit, and your program goes boom. I always allocate buffers to fit the strings that I need to store, leaving the available RAM as the only limit to my code. To this end, functions like strdup() and asprintf() are extremely handy because they already do the allocation for me.
Regarding Point 2, you should try to declare all your loop variables right inside the initialization statement. Like so:
for(int i = 0; i < 3; i++) {
// initialize ith names element with first name
strcpy(final[i], array1[i]);
}
That way you don't run the danger of inadvertently using the loop variable after the loop / forgetting the initialization, etc. because your compiler will complain about the unknown variable.
I have a small program that generates an SHA1 digest for arguments passed via command line and stores them in an array of pointers to char arrays (I think):
#include <stdio.h>
#include <openssl/sha.h>
int entries = 0; // Keep track of entries added
int main(int argc, char **argv)
{
// Allocate space for the digest array
unsigned char **mds = malloc(1);
// Add entries to the digest, one for each argument passed
for(int k = 1; k < argc; k++) {
mds[k - 1] = malloc(SHA_DIGEST_LENGTH);
SHA1(argv[k], strlen(argv[k]), mds[k - 1]);
entries++;
}
// Print each 20-byte digest
for(int j = 0; j < entries; j++) {
for(int i = 0; i < SHA_DIGEST_LENGTH; i++) { printf("%02x ", *(mds[j] + i)); }
printf("\n");
}
}
Originally I had unsigned char **mds = calloc(argc, SHA_DIGEST_LENGTH); and I was going to try to use realloc() everytime I wanted to add another entry (if I didn't know how many entries I was going to have later).
But then I found out that I didn't need to do that and I didn't even need to allocate any space at all? Just a byte and it still works just fine. That doesn't seem right to me.
Am I just lucking out or something? What am I missing?
Am I just lucking out or something?
Yes.
What am I missing?
Your program writes outside of the allocated memory. Doing so causes undefined behaviour. Anything could happen, including the appearance of correct behaviour.
Adding some free() calls will probably turn up some crashers, but no guarantees - undefined behaviour is undefined, after all.
You are writing in memory not allocated to you. Lucky that you have no crashes so far.
Try using valgrind if it is available on your platform. It will tell you about memory errors both of this variety and when you allocate memory that you forget to free. The program will run slower but you only need to do it for testing purposes.
This part:
// Allocate space for the digest array
unsigned char **mds = malloc(1);
allocates a memory block of size 1 byte and casts its address to unsigned char**. Then later in the first iteration already, when you do:
mds[k - 1] = malloc(SHA_DIGEST_LENGTH);
the malloc returns an address, which is written into the invalid memory causing undefined behavior.
You need to allocate appropriate memory block that will hold pointers and in every iteration you will initialize each of these pointers to point to the memory block that will hold string:
// allocate array of pointers:
unsigned char **mds = malloc( (argc - 1) * sizeof(unsigned char*) );
for (int k = 1; k < argc; k++) {
mds[k - 1] = malloc(SHA_DIGEST_LENGTH);
SHA1(argv[k], strlen(argv[k]), mds[k - 1]);
entries++;
}
...
// cleaning up:
for (int k = 1; k < argc; k++) {
free(mds[k - 1]);
}
free(mds);
Ok,
So I am stuck here. I have code for a program that systematically executes people standing in a circle based off an algorithm, but I am having a problem with it crashing in release mode. My code runs fine if I run it using the debugger (codeblocks), but if I don't it crashes. I looked around online, and the only thing I am finding is unintialized variables, but I tried immediately setting values for my variables at declaration and it didn't fix the problem.
If anyone can see what my problem is, I would greatly appreciate help.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// if the program does not work, please run in debugger mode. It will work.
void remove_person(int** array, int arraySize, int position)
{
int i;
for (i = 0; i < arraySize; ++i)
printf("%d ", (*array)[i]);
printf("\n");
int* temp = malloc((arraySize - 1) * sizeof(int)); // create temporary array smaller by one element
memmove(temp,*array,(position+1)*sizeof(int)); // copy entire array before position
memmove(temp+position,(*array)+(position+1),(arraySize - position)*sizeof(int)); // copy entire array after postion
for (i = 0; i < arraySize - 1; ++i)
printf("%d ", (temp)[i]);
printf("\n");
free (*array);
*array = temp;
}
int kill(int** a, int n)
{
int pos = 0;
int round = 1;
while(n > 1)
{
pos = pos + 2 - (round % 2);
while(pos >= n)
pos = pos - n;
remove_person(a,n,pos);
n--;
while(pos >= n)
pos = pos - n;
round++;
}
return *a[0];
}
void main()
{
int n, survivor, i;
int* people;
printf("Enter number of people for Russian Roulette: \n");
scanf("%d", &n);
people = (int*) malloc(n*sizeof(int));
for(i=0; i < n; i++)
{
people[i] = i;
}
survivor = kill(&people, n);
printf("The survivor is person #%d\n", survivor);
}
The basic answer to the title question ("Why do some C programs work in debug but not in release?") is "when they invoke undefined behaviour".
Here, in
memmove(temp,*array,(position+1)*sizeof(int)); // copy entire array before position
memmove(temp+position,(*array)+(position+1),(arraySize - position)*sizeof(int)); // copy entire array after postion
you copy too much. To see why, observe that the first memmove copies to temp[0], temp[1], ..., temp[position], and the second copies to temp[position], temp[position+1], ..., temp[position+arraySize-position-1] = temp[arraySize-1] (note the overlap at temp[position]). But temp only has space for arraySize-1 elements -- you copied one more than it was allowed to hold, so you get undefined behaviour.
It probably works in debug but not release mode because the heap is laid out differently (debug-mode allocators may pad the allocations with extra space to catch bugs like this when running under a debugger or profiler).
The program segfaults if I enter 4 (or even numbers higher than 4) as input, but I won't go into the code to see why that happens.
Apart from that the program works just fine, the problem is that you don't see the output because I think you run it on windows.
That being said you should add something like scanf("%d", &n); at the end or run cmd.exe, go to the directory that holds the executable and run it from there.