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);
Related
I'm trying out triple pointers for the first time.
This question is where I got the info on how to allocate a triple pointer and this is how the triple pointer has been allocated:
//In this case size will always be 4
int size = countLines(file);
printf("size: %d\n", size);
char*** tripleptr = malloc(sizeof(*tripleptr)*size);
int i = 0, k = 0, j = 0;
for(; i < size; i++){
tripleptr[i] = malloc(sizeof(*(tripleptr[i]))*size);
for(; k< size; k++){
tripleptr[i][k] = malloc(sizeof(*(tripleptr[i][k]))*512);
}
}
If I try to copy a string literal into position [0][0] like this
strcpy(tripleptr[0][0], "something");
it works perfectly (same thing for [0][1] to [0][3]), but
strcpy(tripleptr[1][0], "something");
doesn't (it makes the program go into Segmentation Fault).
What is it that could be causing such a weird behavior?
I can't find any indexing mistake in the memory allocation part
The problem is in your loops:
int i = 0, k = 0, j = 0;
for(; i < size; i++){
tripleptr[i] = malloc(sizeof(*(tripleptr[i]))*size);
for(; k< size; k++){
tripleptr[i][k] = malloc(sizeof(*(tripleptr[i][k]))*512);
}
}
You initialized i and k before entering the nested loop, but you don't reset k when the inner loop restarts on the next iteration. This means that you only allocated memory for the first row of strings. So tripleptr[1][0] is uninitialized, and dereferencing that pointer gives you undefined behavior.
Explicitly initialize youf loop control variables each time:
int i, k;
for(i=0; i < size; i++){
tripleptr[i] = malloc(sizeof(*(tripleptr[i]))*size);
for(k=0; k< size; k++){
tripleptr[i][k] = malloc(sizeof(*(tripleptr[i][k]))*512);
}
}
the following proposed code:
illustrates how the block of code should be done
indicates how much memory is actually being allocated
reminds you that the code needs to check for errors
indicates how to handle the error, if one occurs
and now, the proposed code:
//In this case size will always be 4
int size = countLines(file);
printf("size: %d\n", size);
char*** tripleptr = malloc(sizeof(*tripleptr)*size);
for(int i = 0; i < size; i++)
{
tripleptr[i] = malloc(sizeof(*(tripleptr[i]))*size); // is 16 bytes each iteration, assuming a pointer is 4 bytes
for(int k = 0; k< size; k++)
{
tripleptr[i][k] = malloc(sizeof(*(tripleptr[i][k]))*512); // is 4k bytes each iteration
}
}
Note: for robustness:
when calling any of the heap allocation functions: malloc(), realloc(), calloc() : always check (!=NULL) the returned value to assure the operation was successful.
If not successful, call perror( "my error message" ); as that will output to stderr, both your error message and the text reason the system thinks the error occurred. And remember to pass each successfully allocated heap memory pointer to free() before exiting
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 am writing a program in which I have to manage an array of strings. Throughout the program, the strings are systematically removed/modified until only a single string remains.
I remove the strings when they are no longer needed using this function:
void remove_element(char **array, int index, int array_length)
{
int i;
for(i = index; i < array_length - 1; i++){
array[i] = array[i + 1];
}
free(array[array_length]);
}
Once I am totally done with the array, I use this function to ensure all the strings are free'd:
void free_fragments(char **frags){
int i = 0;
while((frags[i] != NULL) && sizeof(frags[i] != 0)){
free(frags[i]);
i++;
}
free(frags);
}
Yet, when I do this, valgrind gives me an "Invalid free() / delete / delete[] / realloc()" error, specifically complaining about the "free(frags[i])" line in free_fragments.
I understand that free_fragments seems kind of redundant, simply because theoretically I can call remove_element on each element until there is one remaining and then remove_element that as well, but when I try to do that, I still get a Invalid free() error, but this time complaining about remove_element. If I don't call free_fragments when I am done with the array, I get a memory leak.
My understanding of pointers is pretty beginner, so please help me debug this and improve my understanding!
The problem is that while you do free(array[array_length]);, you do not NULL it out, so in free_fragments you still think it is a valid pointer.
Come to think of it, you seem to free, the wrong entry, too... This should work:
void remove_element(char **array, int index, int array_length)
{
int i;
free(array[index]);
for(i = index; i < array_length - 1; i++){
array[i] = array[i + 1];
}
array[array_length-1] = NULL;
}
Assuming that index refers to index of string in 2d array which you want to free, I believe that there is a memory leak in your program. Wouldn't the memory address of string to be freed be lost in the first iteration: array[index] = array[index + 1].
Also you are going outside array's boundary in the last statement and freeing entirely something else. Basically there is a memory leak and you should free the string before overwriting its address with neighbouring strings as defined in #LaszloLadanyi's solution.
void remove_element(char **array, int index, int array_length)
{
int i;
for(i = index; i < array_length - 1; i++){
array[i] = array[i + 1];
}
/* Array indexes in C are in the range [0 - length-1]. Here
you are going outside array boundaries.
*/
free(array[array_length]);
}
I think you need to modify you codes to
void remove_element(char **array, int index, int array_length)
{
int i;
for(i = index; i < array_length - 1; i++){
array[i] = array[i + 1]; //I don't understand what's purpose here
}
free_fragments(array); //array[array_length] is a invalid pointer, out of range
}
I am running in a loop the following function:
int* rpermute(int n)
{
int* a = malloc(n * sizeof(int));
int k;
for (k = 0; k < n; k++)
{
a[k] = k;
}
for (k = n - 1; k > 0; k--)
{
int j = rand() % (k + 1);
int temp = a[j];
a[j] = a[k];
a[k] = temp;
}
return a;
}
If I set a new int variable in my code every variable is changing, I assume it is a buffer overflow problem.
Running the valgrind i get the following:
==4459== 73,036 bytes in 19 blocks are definitely lost in loss record 1 of 1
==4459== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4459== by 0x402CFB: rpermute (in /home/giwrgos/Desktop/crowdv22/crowd_evacuation)
==4459== by 0x403378: main (in /home/giwrgos/Desktop/crowdv22/crowd_evacuation)
I use linux through the virtualbox but I have set enough storage and ram, what should I do?
EDIT: See poster's comment below, the problem is not in this code after all.
You should simply free() the memory allocated in and returned by rpermute(). This must be done in the code from where you call rpermute(), once you're done with this array.
I understand that you regenerate this array for differing int values (the n parameter of rpermute()). Perhaps you simply assign a new output to the array you keep:
int* array;
...
array = rpermute(100);
// Some time later.
array = rpermute(200); // Previous array memory is leaking.
You should do instead:
free(array);
array = rpermute(200);
Note that this is not a 'buffer over flow', but a memory leak instead. I checked you code for buffer overflow: index of a points outside it, but this seemed to be ok in both loops.
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]);