I was trying to convert a list of integers into a compact string but I get a segmentation fault.
The code is the following:
int *factors = job_factorization(number, size);
char buffer[250] = { 0 };
for (int i = 0; i < *size; i++) {
sprintf( &buffer[i], "%d ", *factors);
factors++;
}
The job_factorization function returns the head of the list (it works, I have already tested it), and it sets the value pointed to by size to the actual size of the list (so the number of integers).
I cannot figure out what is wrong, does anyone have any idea?
Note these remarks:
sprintf( &buffer[i], "%d ", *factors); does not convert the number at the end of the previous conversion.
sprintf does not check for buffer overflow: if size is large enough, it will eventually write beyond the end of the buffer.
modifying factors is probably not a good idea as this pointer should be freed after use.
Here is an alternative:
int *factors = job_factorization(number, size);
char buffer[1024];
size_t pos = 0;
for (int i = 0; pos < sizeof buffer && i < *size; i++) {
pos += snprintf(buffer + pos, sizeof buffer - pos, "%d ", factors[i]);
}
You could also use 2 loops to compute the size needed for the conversion and allocate the space needed.
Related
I'm studying C at uni and am trying to access the string (the string representation of a binary-number) that was passed into a function to convert it into the integer-representation of that string.
Eg. "011" should return 3.
The string is the first 3 bits in a bitstream that's inputted in reverse.
char * temp_holder = (char *)malloc(sizeof(char) * 4);
int index_of_holder = 0;
for(int i = 2; i >= 0; i--){
printf("%c", buffer[i]);
temp_holder[index_of_holder] = buffer[i];
}
printf("\n");
int decimalValue = fromBinaryToInt(&temp_holder, 3);
printf("DECIMAL_VALUE: %d\n", decimalValue);
The fromBinaryToInt function is:
int fromBinaryToInt(char *string[], int length){
for(int i = 0; i < length; i++){
printf("%c", *string[i]);
}
int int_rep = strtol(*string, (char **)NULL, 2);
printf("REP: %d\n", int_rep);
return int_rep;
}
The subsequent error I get is:
==21==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffda9f47a08 at pc 0x000000500cdf bp 0x7ffda9f47980 sp 0x7ffda9f47978
- READ of size 8 at 0x7ffda9f47a08 thread T0
I thought this could be due to the null-terminating character so I played around with modifying the length variable (+/- 1) in the for-loop within fromBinaryToInt but that hasn't changed anything.
I also considered the for-loop only accessing the first element and nothing more - but my understanding is I've sent through the memory address and the length of the block so the for-loop should have access to the indexes.
Any help would be greatly appreciated,
Cheers :)
In this code:
int index_of_holder = 0;
for(int i = 2; i >= 0; i--){
printf("%c", buffer[i]);
temp_holder[index_of_holder] = buffer[i];
}
index_of_holder is never changed, so all the characters are put in temp_holder[0]. The rest of temp_holder remains uninitialized.
This:
int fromBinaryToInt(char *string[], int length)
declares string to be an array of pointers to char. It is indeed passed &temp_holder, which may be considered to be a pointer to the first element of an array of one pointer to char. However, a more normal usage is to declare a simple pointer to char
int fromBinaryToInt(char *string, int length)
and pass it temp_holder, as in fromBinaryToInt(temp_holder, 3).
As it is, where it is used here:
printf("%c", *string[i]);
This takes element i of the array. When i is 0 in the loop, that is fine, it takes the first element, which exists and is a pointer to char, and then deferences it with * and prints that. However, when i is 1, it attempts to take the second element of the array. That element does not exist, and the resulting behavior is undefined.
If the parameter were merely char *string, then this printf could be:
printf("%c", string[i]);
and, in calling strtol, you would simply pass string rather than *string:
int int_rep = strtol(string, (char **)NULL, 2);
Firstly, bug in below line, index_of_holder remains same all the time, please increment it.
temp_holder[index_of_holder] = buffer[i];
Secondly, in fromBinaryToInt() string is single pointer only so you can't do *string[i]); in the next printf statement.
Here is the working code
int fromBinaryToInt(char *string, int length){
for(int i = 0; i < length; i++){
printf("%c", string[i] ); /*since string is single pointer now you can do like before you did */
}
int int_rep = strtol(string, (char **)NULL, 2);
printf("REP: %d\n", int_rep);
return int_rep;
}
int main() {
char * temp_holder = (char *)malloc(sizeof(char) * 4);
char buffer[4] ="011";
int index_of_holder = 0;
for(int i = 2; i >= 0; i--){
printf("%c", buffer[i]);
temp_holder[index_of_holder] = buffer[i];
index_of_holder++;
}
printf("\n");
int decimalValue = fromBinaryToInt(temp_holder, 3);/* no need to pass address of temp_holder */
printf("DECIMAL_VALUE: %d\n", decimalValue);
return 0;
}
I'm trying to write the values from the string that is read from stdin directly into the array, but I get a segmentation fault. Being that the array is declared after I read N and M, the memory should already be allocated, right?
int main()
{
long long N;
long long M;
scanf("%lld%lld",&N,&M);
char line[M];
long long map[N][M];
for (long long i=0; i<M; i++)
{
scanf("%s", &line);
buildMap(&map, i, &line);
}
for (long long i=0; i<N; i++)
for (long long j=0; j<M; j++)
printf(&map);
}
void buildMap(long long **map, long long i, char * line)
{
for (long long j=0; j<strlen(line); j++)
{
map[i][j] = line[j]-'0';
}
I have read your codes, and I assume you are attempting to build a 2D map via user input, which is a string (named "Line" in your code) that should only contains numbers from 0 to 9. Numbers from 0 to 9 may represent different elements of the map. Am I guessing right?
I copied and modified your code, and finally I managed to get a result like this:
program screenshot
If I am guessing right, let me first explain the reasons why your code can not be successfully complied.
long long M; char line[M];
In here you have used a variable to declare the size of an array. This syntax works in some other programming languages, but not in C. In C, when compling the source code, the compiler must know exactly how much stack memory space to allocate for each function (main() function in your case). Since the complier does not know how large the array is when it is trying to complie your code, you get a compling failure.
One common solution is that, instead of storing array in stack, we choose to store array in heap, because the heap memory is dynamically allocated and released when the program is running. In other words, you can decide how much memory to allocate after you get the user input. Function malloc() and free() are used for this kind of operation.
Another problem is using "long long **map". Though it will not cause complie failure, it won't give you the expected result either. When the M (array width) of the array is a known constant value, we always perfer using "long long map[][M]" as the parameter. However, in your case, with M being unkown, the common solution is to manually calculate the target location, since the elements in an array are always stored in a linear order in memory, regardless of the array demension.
I have fixed the aforementioned two problems, and I am pasting the modified source code below, which has been successfully complied:
#include <malloc.h>
#include <string.h>
void buildMap(int *map, int i, char * line);
int main()
{
int N;
int M;
scanf("%d%d", &N, &M);
/*Since M (available memory space for "Line") is set by user, we need to build
"szSafeFormat" to restrict the user's input when typing the "Line". Assuming M
is set to 8, then "szSafeFormat" will look like "%7s". With the help of
"szSafeFormat", the scanf function will be scanf("%7s", Line), ignoring
characters after offset 7.*/
char szSafeFormat[256] = { 0 };
sprintf(szSafeFormat, "%%%ds", M - 1);
//char line[M];
char *Line = (char *)malloc(sizeof(char) * M); //raw user input
char *pszValidInput = (char *)malloc(sizeof(char) * M); //pure numbers
//long long map[N][M];
int *pnMap = (int *)malloc(sizeof(int) * M * N);
memset(pnMap, 0xFF, M * N * sizeof(int)); //initialize the Map with 0xFF
for (int i = 0; i < /*M*/N; i++)
{
scanf(szSafeFormat, Line); //get raw user input
sscanf(Line, "%[0-9]", pszValidInput); //only accept the numbers
while (getchar() != '\n'); //empty the stdin buffer
buildMap((int *)(pnMap + i * M), i, pszValidInput);
}
printf("\r\n\r\n");
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
//if the memory content is not 0xFF (means it's a valid value), then print
if (*(pnMap + i * M + j) != 0xFFFFFFFF)
{
printf("%d", *(pnMap + i * M + j));
}
}
printf("\r\n");
}
free(Line);
free(pszValidInput);
free(pnMap);
return 0;
}
void buildMap(int *map, int i, char * line)
{
for (int j = 0; j < strlen(line); j++)
{
(int) *((int *)map + j) = line[j] - '0';
}
}
I used type "int" instead of "long long", but there should not be any problems if you insist to continue using "long long". If you continue to use "long long", the condition while printing out the array values should be changed from:
if (*(pnMap + i * M + j) != 0xFFFFFFFF)
to
if (*(pnMap + i * M + j) != 0xFFFFFFFFFFFFFFFF)
There are also some other modifications regarding user input validation, with which I have written some addtional comments in the code.
Remember that C supports variable-length arrays (something which you already use). That means you can actually pass the dimensions as arguments to the function and use them in the declaration of the array argument. Perhaps something like
void buildMap(const size_t N, const size_t M, long long map[N][M], long long i, char * line) { ... }
Call like
buildMap(N, M, map, i, line);
Note that I have changed the type of N and M to size_t, which is the correct type to use for variable-length array dimensions. You should update the variable-declarations accordingly as well as use "%zu for the scanf format string.
Note that in the call to buildMap I don't use the address-of operator for the arrays. That's because arrays naturally decays to pointers to their first element. Passing e.g. &line is semantically incorrect as it would pass something of type char (*)[M] to the function, not a char *.
I'm having difficulty in generating a string of the form "1,2,3,4,5" to pass to a command line program.
Here's what I have tried:
int N=100;
char list[200];
for (i=0; i<2*N; i+=2) {
char tmp;
sprintf(tmp,'%d', i);
strcpy(list[i], tmp);
strcpy(list[i+1], ',');
}
Edit:
I don't feel this question is a duplicate as it is more to do with appending strings into a list and managing that memory and than literally just putting a comma between to integers.
The following code will do what you need.
#include <stdlib.h>
#include <stdio.h>
char* CommaSeparatedListOfIntegers(const int N)
{
if (N < 1)
return NULL;
char* result = malloc(1 + N*snprintf(NULL, 0, "%d,", N));
char* p = result;
for (int i = 1; i <= N; i++)
p += sprintf(p, "%d,", i);
*(p-1) = '\0';
return result;
}
Note that the function returns a heap allocated block of memory that the caller is responsible for clearing up.
Some points of note:
We put a crude upper bound on the length of each number when converted to text. This does mean that we will over allocate the block of memory, but not by a massive amount. If that is a problem for you then you can code a more accurate length. That would involve looping from 1 to N and calling snprintf for each value to determine the required length.
Note that we initially write out a comma after the final value, but then replace that with the null-terminator.
Let's forget about writing strings for the moment and write a function that just prints that list to the screen:
int range_print(int begin, int end, const char *sep)
{
int len = 0;
int i;
for (i = begin; i < end; i++) {
if (i > begin) {
len += printf("%s", sep);
}
len += printf("%d", i);
}
return len;
}
You can call it like this:
range_print(1, 6, ", ");
printf("\n");
The function does not write a new-line character, so we have to do that. It prints all numbers and a custom separator before each number after the first. The separator can be any string, so this function also works if you want to separate your numbers with slashes or tabs.
The function has printf semantics, because it returns the number of characters written. (That value is often ignored, but it can come in handy, as we'll see soon.) We also make the upper bound exclusive, so that in order to print (1, 2, 3, 4, 5) you have tp pass 1 and 6 as bounds.
We'll now adapt this function so that it writes to a string. There are several ways to do that. Let's look at a way that works similar to snprintf: It should tabe a pre-allocated char buffer, a maximum length and it should return the number of characters written or, if the output doesn't fit, the number of characters that would have been written had the buffer been big enough.
int range(char *buf, int n, int begin, int end, const char *sep)
{
int len = 0;
int m, i;
for (i = begin; i < end; i++) {
m = snprintf(buf, n, "%s%d",
(i > begin) ? sep : "", i);
len += m;
buf += m;
n -= m;
if (n < 0) n = 0;
}
return len;
}
This function is tricky because it has to keep track of the number of characters written and of the free buffer still available. It keeps printing after the buffer is full, which is a bit wasteful in terms of performace, but it is legal to call snprintf with a buffer size of zero, and that way we keep the semantics tidy.
You can call this function like this:
char buf[80];
range(buf, sizeof(buf), 1, 6, ", ");
printf("%s\n", buf);
That means that we need to define a buffer that is large enough. If the range of numbers is large, the string will be truncated. We might therefore want a function that allocates a string for us that is long enough:
char *range_new(int begin, int end, const char *sep, int *plen)
{
int len = (end - begin - 1) * strlen(sep) + 1;
char *str;
char *p;
int i;
for (i = begin; i < end; i++) {
len += snprintf(NULL, 0, "%d", i);
}
str = malloc(len);
if (str == NULL) return NULL;
p = str;
for (i = begin; i < end; i++) {
if (i > begin) p += sprintf(p, "%s", sep);
p += sprintf(p, "%d", i);
}
if (plen) *plen = len - 1;
return str;
}
This function needs two passes: in the first pass, we determine how much memory we need to store the list. Next, we allocate and fill the string. The function returns the allocated string, which the user has to free after use. Because the return value is already used, we lose the information on the string length. An additional argument, a pointer to int, may be given. If it is not NULL, the length will be stored.
This function can be called like this.
char *r;
int len;
r = range_new(1, 6, ", ", &len);
printf("%s (%d)\n", r, len);
free(r);
Note that the same can be achieved by calling our old range function twice:
char *r;
int len;
len = range(NULL, 0, 1, 6, ", ");
r = malloc(len + 1);
range(p, len + 1, 1, 6, ", ");
printf("%s (%d)\n", r, len);
free(r);
So, pick one. For short ranges, I recommend the simple range function with a fixed-size buffer.
so I'm trying to write a function that concats a char**args to a char*args
What I have so far is":
char *concat(char **array)
{
int size = 0;
int i=0;
int j=0;
int z=0;
while (array[i]!=NULL)
{
printf(" %s \n", array[i]);
size = size + sizeof(array[i])-sizeof(char); //get the total size, minus the
//size of the null pointer
printf("%d \n",size);
i++;
}
size = size+1; //add 1 to include 1 null termination at the end
char *newCommand = (char*) malloc(size);
i=0;
while(i<sizeof(newCommand))
{
j=0;
z=0;
while (array[j][z]!='\0')
{
newCommand[i] = array[j][z];
i++;
z++;
}
j++;
}
newCommand[sizeof(newCommand)-1]='\0';
return newCommand;
}
this doesn't seem to work. Anyone know what's wrong?
I'd do it like this (untested):
int size = 0;
int count = 0;
while (array[count]) {
size += strlen(array[i]);
count++;
}
char *newCommand = malloc(size + 1);
char *p = newCommand;
newCommand[0] = 0; // Null-terminate for the case where count == 0
for (int i = 0; i < count; i++) {
strcpy(p, array[i]);
p += strlen(array[i]);
}
First, your size calculation was wrong. You wanted the size of the strings, but sizeof(array[i]) gives you the size of a single element in your array which is a pointer and thus 4 (32-bit) or 8 (64-bit). You need to use strlen instead.
Next, your manual copying was also off. It's easier to do it with a moving pointer and strcpy (which is to be avoided normally but we've calculated the sizes with strlen already so it's OK here). The use of strcpy here also takes care of null termination.
Main issue is that you keep using sizeof() with a pointer argument, whereas I think you are trying to get the size of the corresponding array.
sizeof() can only give you information that's available at compile time, such as the sizes of raw types like char and int, and the sizes of arrays with a fixed length such as a char[10]. The sizes of the strings pointed to by a char* is only computable at run time, because it depends on the exact values passed to your function.
For sizeof(newCommand) you probably need size, and for sizeof(array[i]), you probably need strlen(array[i]).
For a program, I would like to make an array copy of the arguments sent in by command line using malloc().
So for example if I do ./a.out one two three
I want an array with {a.out, one, two, three} in it.
However, I have some issues getting my program to work. Here's what I have:
static char** duplicateArgv(int argc, char **argv)
{
char *array;
int j = 0;
// First allocate overall array with each element of char*
array = malloc(sizeof(char*) * argc);
int i;
// For each element allocate the amount of space for the number of chars in each argument
for(i = 1; i < (argc + 1); i++){
array[i] = malloc(strlen(*(argv + i)) * sizeof(char));
int j;
// Cycle through all the chars and copy them in one by one
for(j = 0; j < strlen(*(argv + i)); j++){
array[i][j] = *(argv + i)[j];
}
}
return array;
}
As you might imagine, this doesn't work. I apologize ahead of time if this somehow totally doesn't make sense, as I just started learning about pointers. Also, I'm not quite sure how to write code to free up every element in the *array after I do what I need to the copy.
Could anyone give me some tips on what I should look into to make it do what I want?
Thanks for any help!
You're not allocating or copying the terminating NULL characters:
This line needs to be changed to this for the NULL.
array[i] = malloc((strlen(*(argv + i)) + 1) * sizeof(char));
And the loop should be changed to this:
for(j = 0; j <= strlen(*(argv + i)); j++){
Also, the code can be better optimized if you saved the result of the strlen() call since you call it in so many places.
Try the loop as this:
// For each element allocate the amount of space for the number of chars in each argument
for(i = 0; i < argc; i++){
int length = strlen(argv[i]);
array[i] = malloc((length + 1) * sizeof(char));
int j;
// Cycle through all the chars and copy them in one by one
for(j = 0; j <= length; j++){
array[i][j] = argv[i][j];
}
}
first you need to allocate a vector of char*, not just a char*
char **array;
array = malloc(sizeof(char*)*(argc+1)); // plus one extra which will mark the end of the array
now you have an array[0..argc] of char* pointers
then for each argument you need to allocate space for the string
int index;
for (index = 0; index < argc; ++index)
{
arrray[index] = malloc( strlen(*argv)+1 ); // add one for the \0
strcpy(array[index], *argv);
++argv;
}
array[index] = NULL; /* end of array so later you can do while (array[i++]!=NULL) {...} */
With
char *array;
you define an object of type char*. That is: an object which value can point to a char (and the next char, ..., ...)
You need
char **array;
With this new type, the value of array points to a char*, ie another pointer. You can allocate memory and save the address of that allocated memory in a char*, you't do that with a char.