Unable to understand the code - c

I was trying to make a code to find duplicates in an array and print them. I can't understand this code and why the Calloc is being used and the int main part isn't clear to me.
#include<stdio.h>
#include<stdlib.h>
void printRepeating(int arr[], int size)
{
int *count = (int *)calloc(sizeof(int), (size - 2));
int i;
printf(" Repeating elements are ");
for(i = 0; i < size; i++)
{
if(count[arr[i]] == 1)
printf(" %d ", arr[i]);
else
count[arr[i]]++;
}
}
int main()
{
int arr[] = {4, 2, 4, 5, 2, 3, 1};
int arr_size = sizeof(arr)/sizeof(arr[0]);
printRepeating(arr, arr_size);
getchar();
return 0;
}

The code is actually "dangerous", as it relies on some conditions to hold, e.g. that the maximum integer value in the array is not larger than size-2. So, if your input were, for example, int arr[] = { 114, 112, 114, 5, 2, 3, 1}, then arr[0] would be 114, and the code count[arr[0]]++ would exceed the bounds of the array allocated in printRepeating, which were just 5 elements.
I'd throw the code away and write your own; almost regardless on your experience, your code is likely not worse than the proposed one :-)

The reason for using calloc is that the size isn't known in advance. An alternative would be to use VLA but apparently the author preferred dynamic memory allocation. Kind of bad as the author forgot to free the memory. So this code leaks memory.
Besides that the code is very error prone as it relies on some specific rules for the input (e.g. that no input value is greater that size-2). So my advice is to through this code away and start all over.
BTW: The code uses calloc instead of malloc to get the memory zero initialized.
Regarding main.... This line:
int arr_size = sizeof(arr)/sizeof(arr[0]);
calculates the number of elements in the array.
sizeof(arr) is likely to return 28
sizeof(arr[0]) is likely to return 4
So
28/4 = 7 which is the number of array elements.

This code is really wrong, you shouldn't use it at all.
To give you some hints, this line creates an array:
int *count = (int *)calloc(sizeof(int), (size - 2));
Similar to:
int count[size - 2] // If size was a constant
An unfortunate issue is that the function never disposes the dynamically created array, so you'll see a memory leak.
Line count[arr[i]]++ is a disaster: if the value of arr[i] is greater than size-2 then the software will write to an unassigned memory.
To show how it should be done:
// No value should exceed 50
#define MAX_ARR 50
void printRepeating(int arr[], int size)
{
int found[MAX_ARR];
int i;
// Set found variable to 0
memset(found, 0, sizeof(found));
for(i = 0; i < size; i++)
{
// Did we find the same number before?
if (found[arr[i]] == 1) {
// Yes, print it
printf(" %d ", arr[i]);
} else {
// No, mark is as found
found[arr[i]] = 1;
}
}
}

Actually we use malloc( ) for allocating a single block of specified size that returns a pointer of type of void. This means that we can assign it to any type of type. Form is:
ptr = ( cast - type*) malloc(byte-size)
One very significant difference in malloc() and calloc() is :
While malloc() allocates a single block of storage space with garbage (or random) values, calloc() allocates allocates multiple blocks of storage, each of the same size, and then sets all bytes to zero.

Related

Dynamic memory allocation vs Using pointer

im new to the C language and im trying to understand the basic of memory allocation .
here I have two function that produce the exact same result, but only one of them using the malloc() function and the other one is not.
FIRST FUNCTION
int First()
{
int arr[] = {11, 13, 7, 12, 16};
int length = sizeof(arr) / sizeof(int);
// NOT using malloc
int arr2[length];
// Initialize elements to 0
for (int i=0; i < length; i++)
arr2[i] = 0;
// Print results
for (int i = 0; i < length; i++)
printf("%d ", arr2[i]);
return 0;
}
SECOND FUNCTION
int Second()
{
int arr[] = {11, 13, 7, 12, 16};
int length = sizeof(arr) / sizeof(int);
int *arr2;
// Here im using malloc to allocate memory
arr2 = malloc(sizeof *arr2 * length);
// Initialize elements to 0
for (int i=0; i < length; i++)
arr2[i] = 0;
// Print results
for (int i = 0; i < length; i++)
printf("%d ", arr2[i]);
return 1;
}
Both of this two functions will print: 0 0 0 0 0.
What are the differences between this two approaches ? I know that using malloc(or memset) to allocate memory based on a variable is the right approach , but im trying to understand why exactly ?
Thanks!
The difference is the lifetime of the objects. Basically it means when the object is valid and it can be accessed.
Generally, there are three lifetime types:
Static
The object is valid all the time
Example:
int* foo(void) {
static int A[10];
return A;
}
Array A is always valid. It can be safely returned from a function.
Automatic
The object is valid from its definition to end of the block where it was declared
Example:
void foo(void) {
int *p;
{
int A[10];
... A is valid here ...
p = &A[0]; // p points to
*p = 42; // still valid
}
// A is no longer valid
*p = 666; // UB strikes, expect everything
}
A pointers to automatic objects should never be returned from functions because they would point to non-existing objects. Any use of value of such a pointer triggers Undefined behavior.
Dynamic
The lifetime is controlled by the program. It starts with memory allocation via malloc() or similar functions. It ends when calling free() on the pointer to this object. Dynamic object can only be accessed via a pointer.
Example:
int* foo(void) {
int *p;
{
p = malloc(sizeof(int[10])); // p points to an object for 10 ints
*p = 42; // still valid
}
*p = 43; // still valid
return p; // still valid though function has returned
}
int *p = foo();
*p = 44;
free(p); // release
*p = 666; // UB strikes, expect everything
Forgetting to call free() usually leads to the memory leak.
The second function has a memory leak because the dynamically allocated array was not freed.
The first function is conditionally supported by compilers due to using a variable length array. Also if the size of the array is big then it can occur such a way that the array will not be allocated.
And the function memset allocates nothing.
One of the differences that I can come up with is the sizes of the arrays.
In your first function, if you put sizeof(arr2) at the end, you will get 20. But at your second function, when you put sizeof(arr2) you will get 8. (Size of a pointer depends on your computer. A pointer's size is 8 bytes in 64-bit mode. And 4 bytes in 32-bit mode.)

Inserting in an array struct(Dictionary) in C

So created a Dictionary that had a key and an element. It is ordered by the Key field and the space allocated to the Dictionary must always be equal to the number of elements that it contains. So this is what i did:
//Creating a Dictionary structure
typedef struct dict{
int elem;
int key;
}Dictionary;
int main(){
Dictionary * d = NULL;
int dim = 0;
insert(&d, 5, 3, &dim);
insert(&d, 10, 2, &dim);
insert(&d, 6, 1, &dim);
//insert(&d, 9, 6, &dim);
//insert(&d, 55, 2, &dim);
//insert(&d, 11, 5, &dim);
return 0;
}
void insert(Dictionary **d, int elem, int key, int *dim){
(*d) =(Dictionary *)realloc((*d),(*dim)++);//adding space for another element
int i = 0, j = 0;
while(i < (*dim) && (*d)[i].key < key)//searching for the corect position to insert
i++;
//sliding all the lements to the right
for(j = (*dim); j > i; j--){
(*d)[j] = (*d)[j - 1];
}
//iserting the element in the correct position
(*d)[i].elem = elem;
(*d)[i].key = key;
}
So the code works as it should for the first three elements that insert the problem is that whenerver i try to insert the forth i get a stall like an infinte loop and five or more inserts crashes the program. So could someone explain to me what i am missing or what i am doing wrong ?
with (*d) =(Dictionary *)realloc((*d),(*dim)++); you don't allocate enough memory for the next elements.
After a while you end up corrupt some unallocated/unowned memory and it crashes.
You have to take into account the size of your struct (which must be incremented before calling realloc BTW, another fatal mistake). Since I got tricked by that, I don't propose pre-incrementation, but 2 lines:
(*dim)++;
(*d) = realloc((*d),sizeof(Dictionary)*(*dim));
notes:
you never have to cast the output of realloc, malloc, calloc
reallocating at eah step is easy but not optimal. You should forward allocate more than 1 slot, and reallocate only when your reserve is exhausted. That would avoid calling realloc too often and/or moving the memory around when the block is too small too often.

Iterating through an array w/ pointer

So I'm a beginner with C programming and understanding pointers and how to use them is still giving me some trouble. Here I'm simply trying to iterate through an array using a pointer. I've got this bit of code below but instead of ending at 55, it prints an additional value (32765). First, could someone explain to me why I'm getting an extra value? Second, could someone tell me how to limit this program to the values in the array? I tried using *pntr < 5 for the condition but then nothing prints.
void iterate(int* li) {
for (int *pntr = li; *pntr; pntr++) {
printf("%d\n", *pntr);
}
}
int main(){
int values[] = {11, 22, 33, 44, 55};
iterate(values);
}
This is the right code
void walk(const int *arr, size_t n)
{
int *p;
for (p = arr; p < arr + n; ++p)
printf("%d\n", *p);
}
This is because arr + n gets the address of n integers from the base address of arr.
In addition, int arrays aren't null terminated; only char arrays are when they are used with quotation marks.("abc" rather than {'a', 'b', 'c'}).
This for loop:
for (int *pntr = li; *pntr; pntr++) {
printf("%d\n", *pntr);
}
...expects the array to be zero-terminated. By using *pntr as your test, you're testing that the value pointed to by pntr is not zero. As such, if the array doesn't end with a zero, your loop will wander off past the end of the array until it happens to hit a zero or causes a segfault.
your code is overall not bad, but it's missing one major factor: a working end condition.
In your for loop, when you write *pntr, the end condition will be that the loop will halt when reaching a pointed value of 0. But as your array contains non-zero values, it will get one beyond the allocated memory for your array.
Luckily for you, you only see one extra value, but you might see a lot more values.
To fix the missing end conditions, you have two solutions: either you share the array's length with your iterate function, or you add a value that's known to be the final value of your array.
So either you do:
void iterate(const int *arr, size_t n) {
for (it = arr; it < arr+n ; +=it) printf("$d\n", *it);
}
or you can do:
#define LAST_ITEM -1
int values[] = {11, 22, 33, 44, 55, LAST_ITEM};
void iterate(const int *arr, size_t n) {
for (it = arr; *it != LAST_ITEM ; +=it) printf("$d\n", *it);
}
(you could use #define LAST_ITEM 0, as long as you use a value that's not likely to happen in your array).
Out of these two solutions, the best one is to consider your array with its size.

C: Free temporary array produced by malloc-function

I have a c function that produces my a int array using malloc. It works quiet well and I think it isn't really important what it does because the problem doesn't really have anything to do with that. (In this case it calculates the numbers to a given int and base). I need this array temporary in a function, which might be a sub function of a sub function of a ... (you got the idea, point this function can be used several times) and before the return I would like to run free, but it doesn't work. Here is a testing code (it sorts an array of ints to the amount of ones in their binary representation using qsort (yes I know could have calculated the results more directly, but the point is the probleme I run into when trying to run free (here comment out in function ones))):
#include <stdio.h>
#include <stdlib.h>
int values[] = { 88, 56, 100, 2, 25, 0, 15};
int * baseString(int u, int base);
int abs(int a);
int ones(int a);
int cmpfunc (const void * a, const void * b)
{
return ones(*(int*)a)>ones(*(int*)b);
}
int main()
{
int n;
printf("Before sorting the list is: \n");
for( n = 0 ; n < 7; n++ )
{
printf("%d ", values[n]);
}
qsort(values, 7, sizeof(int), cmpfunc);
printf("\nAfter sorting the list is: \n");
for( n = 0 ; n < 7; n++ )
{
printf("%d (Ones=%d) ", values[n], ones(values[n]));
}
printf("\n");
return(0);
}
int abs(int a){
return (a<0)? a*-1:a;
}
int* baseString(int u, int base){
int* r=malloc(sizeof(int));
r[0]=base;
r[1]=1;
if(base<2){
r[2]=-1;
return r;
}
int negativ=0;
if(u<0){
u=abs(u);
negativ=1;
}
int i=2;
do{
int ur=u%base;
r[i]=ur;
u/=base;
i++;
}while(u>0);
r[1]=i-1;
if(negativ){
r[1]=-r[1];
}
return r;
}
int ones(int a){
int* ai=baseString(a, 2);
int a1=1;
for(int i=2; i<abs(ai[1]); i++){
if(ai[i]==1){
a1++;
}
}
if(!a){
a1=0;
}
//free(ai);
return a1;
}
PS: I am quiet sure this thread is duplicate of some tread somewhere, but I didn't found it.
Part of your problem is actually quite simple.
In your baseString() function, the first three lines are
int* r=malloc(sizeof(int));
r[0]=base;
r[1]=1;
The malloc() dynamically allocates a single int, or an array with one element. The r[1] = 1 modifies the second element of that array which has one element.
The result of that is undefined behaviour. A common symptom of running off the end of an array like this is corrupting memory in your program, such as that used internally by malloc() and free() to keep track of allocated and released memory. Which would explain your problem.
Make sure you allocate the number of elements needed. For example, if 10 elements are needed, malloc(10*sizeof(int)). You need to work out the number needed, since dynamically arrays will not magically grow to get the number of elements needed.
I haven't look further, so there may be other problems. But this one is pretty glaring.
It is also a good idea to check that malloc() actually succeeds. It returns NULL if it fails.
The key problem here appears to an ABW (Array Bounds Write). In the baseString function, you are actually allocating memory which is equivalent to the size of 1 integer, but are trying to access it like an array in r[1],r[2], r[i] etc, which results in write to memory which technically doesn't belong to you.
The code snippet in your code corresponds to
int* r=malloc(sizeof(int));
r[0]=base;
r[1]=1; //ABW here
if(base<2){
r[2]=-1; //ABW here
return r;
}
do{
int ur=u%base;
r[i]=ur; //ABW here
u/=base;
i++;
}while(u>0);
This might lead to undefined behavior at any point of time in your code. In your case, it seems to be affecting free as the memory overwrite may have messed up with the internal book keeping data of malloc and free implementation.

Why can't I use malloc to set array size larger than what is needed?

I am coming from Python so using malloc is new to me. Intuitively the below should work but having syntax issues. In my first line I want to set array size to be a max of 8 ints. In the second line, I want to add those 4 ints. This line is for example only, in production I will have user input up to an 8-digit number. When I go to compile (clang) I get size of array has non-integer type 'void *' If I comment out this first line and initialize with the second line (and adding int type) the code works. So I am obviously setting the size incorrectly. Any ideas?
int main(void)
{
int mult_digits[malloc(8 * sizeof(int))];
mult_digits[] = {1,2,3,4};
int size_mult = sizeof mult_digits / sizeof *mult_digits;
printf("Size of the array is %d\n", size_mult);
return 0;
}
This code is all wrong. You call malloc to allocate memory, and malloc returns a pointer. Rather than deconstructing your syntax, which is very broken, I'll give a couple of variants of your program.
int main(void)
{
int mult_digits[] = {1,2,3,4};
int size_mult = sizeof mult_digits / sizeof *mult_digits;
printf("Size of the array is %d\n", size_mult);
return 0;
}
Here the array is not allocated dynamically. It's a local variable with automatic, stored on the stack.
For dynamic allocation you would do this:
int main(void)
{
int *mult_digits = malloc(4*sizeof *mult_digits);
mult_digits[0] = 1;
mult_digits[1] = 2;
mult_digits[2] = 3;
mult_digits[3] = 4;
free(mult_digits);
return 0;
}
The argument to malloc is the number of bytes to be returned. The value returned is the address of the new block of memory. Note also here that we made a call to free to deallocate the memory. If you omit that, the memory will be leaked.
With this variant, there is no way to recover the length of the array from mult_digits. I know that might freak you out, coming from Python, but I repeat. There is no way to recover the length of the array from mult_digits. It's your job to keep track of that information.
Now, you wanted to over-allocate the memory. You can certainly do that:
int main(void)
{
int *mult_digits = malloc(8*sizeof *mult_digits);
mult_digits[0] = 1;
mult_digits[1] = 2;
mult_digits[2] = 3;
mult_digits[3] = 4;
free(mult_digits);
return 0;
}
Here we only used the first 4 elements, and ignored the final 4. That's just fine. In case you do over-allocate you would typically need to keep track of both the allocated length, and the in-use length. You can then add new items by increasing the in-use length, up until you reach the allocated length. The you need to reallocate a larger block. I guess that's what you are driving at.
The problem is your syntax. What you meant was: int *mult_digits = malloc(8 * sizeof(int));
After that, mult_digits[] = {1,2,3,4}; is wrong. You could, however,
mult_digits[0] = 1;
mult_digits[1] = 2;
mult_digits[2] = 3;
mult_digits[3] = 4;
But unless you have some reason to swim into the pointer deep end, you might just want to:
int mult_digits[] = {1, 2, 3, 4};
Edit: to help with applying this answer, here is a full modified function that compiles and runs:
#include <stdio.h>
int main(void)
{
int mult_digits[] = {1,2,3,4};
int size_mult = sizeof mult_digits / sizeof *mult_digits;
printf("Size of the array is %d\n", size_mult);
return 0;
}
in square brackets you must specify the number of elements needed, not the length in byte of the array. you can use malloc to do
int* multdigits = malloc(sizeof(int)*8);
mult_digits[0] = 1;
etc
(sorry if my syntax is wrong, i don't write c code from years!)
they're two ways to do the same thing. see "malloc" as the "new" operator.
Try
int main(void)
{
int mult_digits[] = {1,2,3,4};
int size_mult = sizeof mult_digits / sizeof int;
printf("Size of the array is %d\n", size_mult);
return 0;
}

Resources