i just started learning c. i am doing an exercise and the question is as follows.
Write a function called insertString to insert one character string into another string.The arguments to the function should consist of the source string, the string to be inserted, and the position in the source string where the string is to be inserted. So, the call insertString (text, "per", 10); with text as originally defined "the wrong son" results in the character string "per" being inserted inside text, beginning at text[10].Therefore, the character string "the wrong person" is stored inside the text array after the function returned.
#include<stdio.h>
int insertString(char[],char[],int);
int stringLength(char[]);
int main()
{
char text[]="the wrong son";
int result=insertString(text,"per",10);
if(result!=-1)
printf("string 1 is : %s \n",text);
else
printf("Not possible\n");
return 0;
}
int insertString(char a[],char b[],int pos)
{
int i=0,j=0;
int lengthA=stringLength(a);
int lengthB=stringLength(b);
if(pos>lengthA)
return -1;
for(i=lengthA;i>=pos;i--)
a[i+lengthB]=a[i];
for ( i = 0; i < lengthB; ++i )
a[i + pos] = b[i];
return 1;
}
int stringLength(char x[])
{
int length=0;
while(x[length]!='\0')
length++;
return length;
}
i have done this and it's working too. but i am receiving a message abort trap : 6. when i looked upon it, i learned it's an error because i am writing to the memory that i don't own. since i have used variable length character arrays, wherever the null character is, indicates the end of array and i am trying to extending it by inserting a string, that's my understanding. am i right so far?
i am also moving the null character. i don't know whether it's right or wrong.
so is there a way to get around this error? Also, i don't know pointers yet and they're in the next chapter of the textbook .
Any help in this would be appreciated very much.
A variable-length array is a very specific C construct that has nothing to do with what your textbook calls "variable length arrays". If I were you I would not trust this textbook if it said that 1+1=2. So much for it.
A character array that ends with a null character is called string by pretty much everyone, everywhere.
char text[]="the wrong son";
Your textbook led you to believe that text will hold as many characters as you need. Alas, there is no such thing in C. In fact text will hold exactly as many characters as there are in its initializer, plus 1 for the null terminator, so you cannot insert anything in it.
In order for your program to work, you need to explicitly allocate as many characters for text as the resulting string will contain.
So as there are 14 characters in "the wrong son" (including the terminator) and three characters in "per" (not including the terminator), you need 17 characters in total:
char text[17]="the wrong son";
You can also check your calculations:
int result=insertString(text, "per", 10, sizeof(text));
...
int insertString(char a[], char b[], int pos, int capacity)
{
...
if (lengthA + lengthB + 1 < capacity)
return -1;
...
First you must understand what the difference between C-programming and other programming languages are manual memory management and pointers.
In C you have to do everything yourself but you have total control of everything, in other languages like Java a lot is made automatically for you but you can't open the hood.
Memory handling in C is the essence of C and is very different from for instance Java that looks very alike C. Java and C syntax are very much alike, but in two completely different worlds.
C++ is an extension of C that allow similar features like in Java, but still memory wise is C.
There are two types of memory in C:
Automatic string array (declared as char xx[]) with the exact length of its initialization number or defined string length (the number between the []) + 1 (for null termination), can't be changed.
Dynamic memory (declared as char*) is allocated with calloc() or alloc() and can be changed in length with realloc() and must be manually freed or else the program will leak memory (still allocate memory after the program is ended (some OS has automatic clean-up of that but it is bad style C programming not freeing memory)).
Dynamic memory is delivered to a pointer (char*) that points to the memory allocated. Pointers can point at any type of memory also string arrays and even integers.
A pointer is an integer, a number pointing at the available memory address in the OS, the OS keep track of the memory of each pointer, but do not clean it up like in Java.
Also note that after the realloc the old memory of the old pointer is freed by the command, new is allocated that you must manually free, later after use.
It is possible to send a pointer (it is just a number) into a function and the function changes the pointer (it is just another number pointing at memory (that might not be the same)).
Because of this it is essential to return the new pointer from functions that might have changed its content.
In practice the core of C-programming is pointer programming and the programmer must have a firm track of the memory or the program goes berserk, you have to learn the routines.
With pointer programming it is possible to have absolute control over all the memory and the functions becomes normally very efficient, fast and memory lean.
This is used also when we are talking about huge data like in high resolution pictures or video content, and often the only way to get performance.
Extended level - pointer to pointers
When getting more advanced it is possible to send the pointer of a pointer (char**) to a function allowing the function to amend the content of a pointer like reallocation of a string and the updated pointer will be readable by the calling function. This way multiple pointers can be amended (there is only one return value).
A pointer to a pointer, points to the memory where the pointer address (the number that points to the memory) is stored, so sending it into the function the function can change the pointers number (what memory it points at) and the calling function can read it (the same pointer have a new value).
Pointers to pointers are normally used for instance in database programming with linked lists being able to control a huge number of memory chunks in a long chain, and being able to handle them smoothly.
Most other programming languages basic system is programmed in C, so normally it is possible to integrate chunks of C--code to improve performance.
ANSI C is the same in all computers so it is also a way of making code real portable from system to system and work the same in them all.
Lets check out your case, here is a sample code to show.
#include<stdlib.h>
#include<string.h>
#include <stdio.h>
char* insertString(
char* pTarget,
char* pInput);
void main(void)
{
char Target[9] = { "Hello" };
char Target2[9] = { "Hi" };
char Input[] = { " World" };
Target and Target2 are automatic string arrays with the exact length of its initialization number, the number between the[] and Input is defined by the string length(+1 for null termination), can't be changed.
So, the length of Input is defined as 7 bytes, six letters +1, as Target and Target2 are defined as 9 bytes (can contain 8 letters), can't be changed, they are string arrays.
This below will not work, because Target is too short, only 9 chars space (enough for 8 letters) and Target + Input is 11 letters, the program will crash.
strcat(Target, Input);
But this will work because Target2 is 9 chars (space for 8 letters) and Target2 + Input is 8 letters, so it fits.
strcat(Target2, Input);
printf("%s\n", Target2);
This below will not work because Target is an automatic char array with the exact length of its initialization number or string length +1 (for null termination), its length can't be changed.
They are fixed in length and not possible to extend or shrink in length, can't realloc them, and they will be freed automatically at the end of the function.
In fact, it is created normally in another set of memory than the dynamic memory and is protected from change.
pTarget = insertString(Target, Input);
{
This below will work because it is dynamically allocated memory(by a calloc or alloc command) that can be reallocated to any size.
Dynamic memory(volatile) in C is not automatic like in other programming languages, must be taken care of manually.
Usually in C a p is put ahead for pointers to differentiate them from automatic string arrays.
Dynamically allocated memory must be manually freed after use or the program will leak memory, it is not Java with auto clean - up.
char* pTarget = calloc(strlen(Target) + 1, sizeof(char));
if (pTarget) {
strcpy(pTarget, Target);
pTarget = insertString(pTarget, Input);
Also notice you as a programmer must check that you got the memory you asked for by the memory allocation command calloc.
If not (very unlikely memory is unavailable in 2022) you can't perform the action and, you fail, or the program will crash.
printf("%s\n", pTarget);
free(pTarget);
}
else
printf("%s\n", "Failure!");
}
}
char* insertString(
char* pTarget,
char* pInput)
{
We are here reallocating the memory to get it enlarged to fit our use
pTarget = realloc(pTarget, (strlen(pTarget) + strlen(pInput) + 1) * sizeof(char));
The old memory is freed by realloc and a new larger is allocated for us.
Now the pointer (the number that points to memory) might not be the same as before realloc.
A pointer is a storage of the number and the same storage pTarget contains the new number to the new data, OK.
if (pTarget)
strcat(pTarget, pInput);
return pTarget;
}
int replace_substring (char *str, char *substr, char *new_substr) {
int pos = delete_substring (str, substr); /* first delete the existing substring */
if (pos == -1) return pos; /* substring not found, return */
insert_substring (str, pos, new_substr); /* add the new substring at the deleted position */
}
int replace_substring (char *str, char *substr, char *new_substr) {
int pos = delete_substring (str, substr); /first delete the existing substring/
if (pos == -1) return pos; /substring not found, return/
insert_substring (str, pos, new_substr); /add the new substring at the deleted position/
}
Related
A little more than 20 years ago I had some grasp of writing something small in C , but even at that time, I probably didn't really do things right all the time. Now I'm trying to learn C again, so I'm really a newbie.
Based on this article:
Using realloc to shrink the allocated memory
, I made this test, which works, but troubles me:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int test (char *param) {
char *s = malloc(strlen(param));
strcpy(s, param);
printf("original string : [%4d] %s \n", strlen(s), s);
// reduce size
char *tmp = realloc(s, 5);
if (tmp == NULL) {
printf("Failed\n");
free(s);
exit(1);
} else {
tmp[4] = 0;
}
s = tmp;
printf("the reduced string : [%4d] %s\n", strlen(s), s );
free(s);
}
void main(void){
test("This is a string with a certain length!");
}
If I leave out "tmp[4] = 0", then I still get back the whole string. Does this mean the rest of the string is still in memory, but not allocated anymore?
how does c free memory anyway? Does it keep track of memory by itself or is it something that is handled by the OS?
I free the s string "free(s)", do I also need to free the tmp str (it does point to the same memory block, yet the (same) address it holds is probably stored on another memory location?
These are most likely just basics, but none of what I have read so far has given me a clear answer (including mentioned article).
If I leave out "tmp[4] = 0", then I still get back the whole string.
You've invoked undefined behavior. All the string operations require the argument to be a null-terminated array of characters. If you reduce the size of the allocation so it doesn't include the null terminator, you're accessing outside the allocation when it tries to find it.
Does this mean the rest of the string is still in memory, but not allocated anymore?
In practice, many implementations don't actually re-allocate anything when you shrink the size. They simply update the bookkeeping information to say that the allocated length is shorter, and return the original pointer. So the remainder of the string stays the same unless you do another allocation that happens to use that memory.
This can even happen when you grow the size. Some designs always allocate memory in specific granularities (e.g. powers of 2), so if you grow the allocation but it doesn't exceed the granularity, it doesn't need to copy the data.
how does c free memory anyway? Does it keep track of memory by itself or is it something that is handled by the OS?
Heap management is part of the C runtime library. It can use a variety of strategies.
I free the s string "free(s)", do I also need to free the tmp str (it does point to the same memory block, yet the (same) address it holds is probably stored on another memory location?
After s = tmp;, both s and tmp point to the same allocated memory block. You only need to free one of them.
BTW, the initial allocation should be:
char *s = malloc(strlen(param)+1);
You need to add 1 for the null terminator, since strlen() doesn't count this.
#include <stdio.h>
int main ()
{
char str[40];
printf("Enter a string : \n");
gets(str);
printf("You entered: %s\n", str);
return 0;
};
in above code, if replace str to a pointer, char *str. Then NULL is out. Suppose gets defined by char *gets(char *str), it should use a pointer instead of array. All examples I saw are array not pointers. Thanks.
function gets() is depracted your libc/compiler might ignore it. try use fgets() instead.
#include <stdio.h>
int main ()
{
char str[40];
printf("Enter a string : \n");
if (fgets(str, sizeof(str), stdin) != NULL)
{
printf("You entered: %s\n", str);
}
return 0;
};
also if you want to don't use stack you need to give pointer that points allocated space. in code str also can be char *str = malloc(40); then change sizeof(str) to 40 since str is no longer stack.
Really interesting question, I have been asked this question a lot!
you should have a bit background of pointers and memory to understand what is happening.
first let's have a brief review about pointers and memory:
our computer have some memory and we can use it in programming, anything that we store (in runtime) for example an int, array of doubles, some complex struct and strings(that they are array of characters) should be somewhere in memory.
pointers contain address of somewhere in memory, some of them know about that memory (how to read/write value) some of them don't.
there is a special value for pointers (NULL) that means nowhere, if pointer is pointing to NULL, that pointer is pointing not nowhere (obviously nowhere is not a valid address in memory)
array is specific type of pointer, a const pointer that is pointing to already allocated memory in stack.
and about gets function: let's think we want to re-implement such function (namely my_gets) , how we suppose to do that? how to return a string (array of characters)?
these are options (as far as i know):
creating a local array in our function and fill it. then we should return it? no we cant! because that array is in stack of our function and after ending the function, our function data including this array will be popped automatically (handled by compiler).
although nobody forbid us from returning that array, but that would cause dangling pointer problem.
allocating some space rather than stack (heap) and fill that. this is perfectly fine and there is methods and do this! for example readline (not in ansi c, you can find it here) will do this. the drawback of this method is that you should take care of that memory and free it later, it also may be not to optimum way and you may should copy that string to your already allocated memory
the last way (and way that gets use) is getting a pointer that is already pointing to a valid memory and fill that memory. you already know that gets want a pointer as input, I add that, that pointer should point to a valid and accessible memory that gets can fill it. if pointer is pointing to NULL (or maybe uninitialized and pointing to some where random) gets will fail writing and cause undefined behavior (segmentation fault for example)
some final points:
array solution work because array name is pointer that pointing to valid memory (array in stack) so it's OK and easy to understand.
If we don't want to use array, we can point our pointer to a valid memory, we need to use malloc/calloc to allocate a block of memory. see this:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int size = 40 * sizeof(char);
char* p = malloc(size);
printf("Enter a string : \n");
if (fgets(p, size, stdin) != NULL) {
printf("You entered: %s\n", p);
}
free(p);
return 0;
}
gets is not secure because it doesn't care how much memory we have, it writes until and string ends and it may cause buffer overflow, better option (as people said) is fgets because it care memory size and will not exceed that. but my answer doesn't care it's fgets or gets.
I'm new to C (and also programming). Recently I practise a lot in leetcode(oj.leetcode.com) by using C. While I was coding below problem:
http://oj.leetcode.com/problems/reverse-words-in-a-string/
It means if the input is "the sky is blue", the output should be "blue is sky the". I decided to use two-dimensional char array to store the words and print them in reversed order. Here is the code:
void reversed(char s[]) {
int wordcount=0;
char** words;
int i=0;
for(i=0;i<strlen(s);i++)
{
if(!isspace(s[i]))
{
**words=s[i];
*words++;
}
else
{
**words='\0';
wordcount++;
words++;
}
}
for(;wordcount=0;wordcount--,words--)
{
printf("%s", *words);
printf(" ");
}
printf("\n");
But I encountered a segmentation fault, here is the GDB info:
23 for(i=0;i<strlen(s);i++)
(gdb) n
25 if(!isspace(s[i]))
(gdb) n
27 **words=s[i];
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x00401226 in reversewords (s=0x22abe0 "the sky is blue") at reversewords.c:27
27 **words=s[i];
Can anybody can help me to figure out what's going on here and how to correct my code to make it work? Thanks in advance!!
You have to allocate space for your array. This is usually done using malloc:
// dynamically allocated array of 10 ints
int* dynamicIntArray = malloc(sizeof(int) * 10);
The problem here is that you don't know the number of elements beforehand. The general solution is to allocate a certain amount of space, and then reallocate more in case it is not sufficient (using realloc, for example). So your flow becomes:
Allocate some initial space using an arbitrary starting size: char** words = malloc(sizeof(char*) * 100);
Every time you add something to the array, check if you already have 100 elements. If so: words = realloc(words, lastSize * 2);
When you're done, free the memory: free(words);
Some notes
You mustn't increment words for the free to work correctly. So you have to either use a second pointer (char** pWords = words; then: pWords++) or use numeric indices instead of pointer arithmetic.
Allocating twice the original buffer's size is a common heuristic to keep the number of reallocations small.
In your case, you could in principle determine the number of words beforehand by counting spaces, but then you'll need to go over the input string twice.
As a side note: If all this seems daunting, keep in mind you don't have to handle this complexity when working in more high-level languages. In C++ for example, you can use a std::vector which will automatically grow as you add elements, and automatically free it's memory.
What you've done here is that take a pointer to pointer to character and then started working with it.
Why is it wrong ?
You have a pointer (or more precisely pointer to pointer) to char, but you never told it where to point to. Right now for all we know, it is pointing to some memory that you are not supposed to point to, or maybe some other junk memory location.
What do we do?
Use malloc, allocate memory to your pointer and then use it.
Keep in mind that when you have pointer to pointer to character, you'll have to malloc twice. The first one will make your pointer point to the placeholders for pointers that will point to characters. (Read again, it might make sense :P )
ie. if I do
char *ptr;
In this case, I'll do
ptr = malloc(sizeof(char) * 10) //considering your words are max length 10.
But since you want multiple words, hence you want a pointer that will point to an array of such pointers.
char **ptrSuper
ptrSuper = malloc(sizeof(char*) * 4) // considering you have 4 words.
Overall, this is what you'll have to do
char **ptr
ptr = malloc(sizeof(char*) * 4)
for(i=0 ; i<4 ; i++)
ptr[i] = malloc(sizeof(char)*10)
So I searched for one minute, and I found the following pic from some source that will make it a bit more lucid.
Link for Image.
As mentioned in the comments below, You don't need to do allocation for each word that you are to reverse. Since you'll get the reference of that from the character array you are passing. But just in case.
I'm trying to learn C and as a start, i set off writing a strcpy for my own practice. As we know, the original strcpy easily allows for security problems so I gave myself the task to write a "safe" strcpy.
The path I've chosen is to check wether the source string (character array) actually fits in the destination memory. As I've understood it, a string in C is nothing more than a pointer to a character array, 0x00 terminated.
So my challenge is how to find how much memory the compiler actually reserved for the destination string?
I tried:
sizeof(dest)
but that doesn't work, since it will return (as I later found out) the size of dest which is actually a pointer and on my 64 bit machine, will always return 8.
I also tried:
strlen(dest)
but that doesn't work either because it will just return the length until the first 0x0 is encountered, which doesn't necessarily reflect the actual memory reserved.
So this all sums up to the following question: How to find our how much memory the compiler reserved for my destination "string"???
Example:
char s[80] = "";
int i = someFunction(s); // should return 80
What is "someFunction"?
Thanks in advance!
Once you pass a char pointer to the function you are writing, you will loose knowledge for how much memory is allocated to s. You will need to pass this size as argument to the function.
You can use sizeof to check at compile time:
char s[80] = "";
int i = sizeof s ; // should return 80
Note that this fails if s is a pointer:
char *s = "";
int j = sizeof s; /* probably 4 or 8. */
Arrays are not pointers. To keep track of the size allocated for a pointer, the program simply must keep track of it. Also, you cannot pass an array to a function. When you use an array as an argument to a function, the compiler converts that to a pointer to the first element, so if you want the size to be avaliable to the called function, it must be passed as a parameter. For example:
char s[ SIZ ] = "";
foo( s, sizeof s );
So this all sums up to the following question: How to find our how much memory the compiler reserved for my destination "string"???
There is no portable way to find out how much memory is allocated. You have to keep track of it yourself.
The implementation must keep track of how much memory was malloced to a pointer, and it may make something available for you to find out. For example, glibc's malloc.h exposes
size_t malloc_usable_size (void *__ptr)
that gives you access to roughly that information, however, it doesn't tell you how much you requested, but how much is usable. Of course, that only works with pointers you obtained from malloc (and friends). For an array, you can only use sizeof where the array itself is in scope.
char s[80] = "";
int i = someFunction(s); // should return 80
In an expression s is a pointer to the first element of the array s. You cannot deduce the size of an array object with the only information of the value of a pointer to its first element. The only thing you can do is to store the information of the size of the array after you declare the array (here sizeof s) and then pass this information to the functions that need it.
There's no portable way to do it. However, the implementation certainly needs to know this information internally. Unix-based OSes, like Linux and OS X, provide functions for this task:
// OS X
#include <malloc/malloc.h>
size_t allocated = malloc_size(somePtr);
// Linux
#include <malloc.h>
size_t allocated = malloc_usable_size(somePtr);
// Maybe Windows...
size_t allocated = _msize(somePtr);
A way to tag the member returned by malloc is to always malloc an extra sizeof(size_t) bytes. Add that to the address malloc returns, and you have a storage space for storing the actual length. Store the malloced size - the sizeof (size_t) there, and you have the basis for your new set of functions.
When you pass two of these sorts of pointers into your new-special strcpy, you can subtract sizeof(size_t) off the pointers, and access the sizes directly. That lets you decide if the memory can be copied safely.
If you are doing strcat, then the two sizes, along with calculating the strlens means you can do the same sort of check to see if the results of the strcat will overflow the memory.
It's doable.
It's probably more trouble than it's worth.
Consider what happens if you pass in a character pointer that was not mallocated.
The assumption is that the size is before the pointer. That assumption is false.
Attempting to access the size in that case is undefined behavior. If you are lucky, you may get a signal.
One other implication of that sort of implementation is that when you go to free the memory, you have to pass in exactly-the-pointer-that-malloc-returned. If you don't get that right, heap corruption is possible.
Long story short...
Don't do it that way.
For situations where you are using character buffers in your program, you can do some smoke and mirrors to get the effect that you want. Something like this.
char input[] = "test";
char output[3];
if (sizeof(output) < sizeof(input))
{
memcpy(output,input,sizeof(input) + 1);
}
else
{
printf("Overflow detected value <%s>\n",input);
}
One can improve the error message by wraping the code in a macro.
#define STRCPYX(output,input) \
if (sizeof(output) < sizeof(input)) \
{ \
memcpy(output,input,sizeof(input) + 1); \
} \
else \
{ \
printf("STRCPYX would overflow %s with value <%s> from %s\n", \
#output, input, #input); \
} \
char input[] = "test";
char output[3];
STRCPYX(output,input);
While this does give you what you want, the same sort of risks apply.
char *input = "testing 123 testing";
char output[9];
STRCPYX(output,input);
the size of input is 8, and output is 9, the value of output ends up as "Testing "
C was not designed to protect the programmer from doing things incorrectly.
It is kind of like you are attempting to paddle upriver :)
It is a good exercise to think about.
Although arrays and pointers can appear to be interchangeable, they differ in one important aspect; an array has size. However because an array when passed to a function "degrades" to a pointer, the size information is lost.
The point is that at some point you know the size of the object - because you allocated it or declared it to be a certain size. The C language makes it your responsibility to retain and disseminate that information as necessary. So after your example:
char s[80] = ""; // sizeof(s) here is 80, because an array has size
int i = someFunction(s, sizeof(s)) ; // You have to tell the function how big the array is.
There is no "magic" method of determining the size of the array within someFunction(), because that information is discarded (for reasons of performance and efficiency - C is relatively low level in this respect, and does not add code or data that is not explicit); if the information is needed, you must explicitly pass it.
One way in which you can pass a string and retain size information, and even pass the string by copy rather than by reference is to wrap the string in a struct thus:
typedef struct
{
char s[80] ;
} charArray_t ;
then
charArray_t s ;
int i = someFunction( &s ) ;
with a definition of someFunction() like:
int someFunction( charArray_t* s )
{
return sizeof( s->s ) ;
}
You don't really gain much by doing that however - just avoid the additional parameter; in fact you loose some flexibility because someFunction() now only takes a fixed array length defined by charrArray_t, rather than any array. Sometimes such restrictions are useful. On feature of this approach is that you can pass by copy this:
int i = someFunction( s ) ;
then
int someFunction( charArray_t s )
{
return sizeof( s.s ) ;
}
since structures unlike arrays can be passed this way. You can equally return by copy as well. It can be somewhat inefficient however. Sometimes the convenience and safety outweigh the inefficiency however.
I have a structure that has an array of pointers. I would like to insert into the array digits in string format, i.e. "1", "2", etc..
However, is there any difference in using either sprintf or strncpy?
Any big mistakes with my code? I know I have to call free, I will do that in another part of my code.
Many thanks for any advice!
struct port_t
{
char *collect_digits[100];
}ports[20];
/** store all the string digits in the array for the port number specified */
static void g_store_digit(char *digit, unsigned int port)
{
static int marker = 0;
/* allocate memory */
ports[port].collect_digits[marker] = (char*) malloc(sizeof(digit)); /* sizeof includes 0 terminator */
// sprintf(ports[port].collect_digits[marker++], "%s", digit);
strncpy(ports[port].collect_digits[marker++], digit, sizeof(ports[port].collect_digits[marker]));
}
Yes, your code has a few issues.
In C, don't cast the return value of malloc(). It's not needed, and can hide errors.
You're allocating space based on the size of a pointer, not the size of what you want to store.
The same for the copying.
It is unclear what the static marker does, and if the logic around it really is correct. Is port the slot that is going to be changed, or is it controlled by a static variable?
Do you want to store only single digits per slot in the array, or multiple-digit numbers?
Here's how that function could look, given the declaration:
/* Initialize the given port position to hold the given number, as a decimal string. */
static void g_store_digit(struct port_t *ports, unsigned int port, unsigned int number)
{
char tmp[32];
snprintf(tmp, sizeof tmp, "%u", number);
ports[port].collect_digits = strdup(tmp);
}
strncpy(ports[port].collect_digits[marker++], digit, sizeof(ports[port].collect_digits[marker]));
This is incorrect.
You have allocated onto collect_digits a certain amount of memory.
You copy char *digits into that memory.
The length you should copy is strlen(digits). What you're actually copying is sizeof(ports[port].collect_digits[marker]), which will give you the length of a single char *.
You cannot use sizeof() to find the length of allocated memory. Furthermore, unless you know a priori that digits is the same length as the memory you've allocated, even if sizeof() did tell you the length of allocated memory, you would be copying the wrong number of bytes (too many; you only need to copy the length of digits).
Also, even if the two lengths are always the same, obtaining the length is this way is not expressive; it misleads the reader.
Note also that strncpy() will pad with trailing NULLs if the specified copy length is greater than the length of the source string. As such, if digits is the length of the memory allocated, you will have a non-terminated string.
The sprintf() line is functionally correct, but for what you're doing, strcpy() (as opposed to strncpy()) is, from what I can see and know of the code, the correct choice.
I have to say, I don't know what you're trying to do, but the code feels very awkward.
The first thing: why have an array of pointers? Do you expect multiple strings for a port object? You probably only need a plain array or a pointer (since you are malloc-ing later on).
struct port_t
{
char *collect_digits;
}ports[20];
You need to pass the address of the string, otherwise, the malloc acts on a local copy and you never get back what you paid for.
static void g_store_digit(char **digit, unsigned int port);
Finally, the sizeof applies in a pointer context and doesn't give you the correct size.
Instead of using malloc() and strncpy(), just use strdup() - it allocates the buffer bin enough to hold the content and copies the content to the new string, all in one shot.
So you don't need g_store_digit() at all - just use strdup(), and maintain marker on the caller's level.
Another problem with the original code: The statement
strncpy(ports[port].collect_digits[marker++], digit, sizeof(ports[port].collect_digits[marker]));
references marker and marker++ in the same expression. The order of evaluation for the ++ is undefined in this case -- the second reference to marker may be evaluated either before or after the increment is performed.