I'm trying to learn about the snprintf and found this answer with the example:
char buf[20] = "";
char *cur = buf, * const end = buf + sizeof buf;
cur += snprintf(cur, end-cur, "%s", "foo");
printf("%s\n", buf);
if (cur < end) {
cur += snprintf(cur, end-cur, "%s", " bar");
}
printf("%s\n", buf);
free(str);
The thing that is not clear to me is that we allocate a fixed hardcoded buffer size which seems suffer from buffer overflow. In the N1570 I found that (7.21.6.5)
1
#include <stdio.h>
int snprintf(char * restrict s, size_t n,
const char * restrict format, ...);
2 The snprintf function is equivalent to fprintf, except that the
output is written into an array (specified by argument s) rather than
to a stream. If n is zero, nothing is written, and s may be a null
pointer.
So to me it appears as the idiomatic usage would be as follows:
int need_space = snprintf(NULL, 0, "abs %s", "fgh") + 1; //How much to allocate?
char *const str = malloc(need_space * sizeof(char)); //allocate
int written = snprintf(str, need_space, "abs %s", "fgh"); //do format
printf("Need space = %d, written = %d\n", need_space, written);
printf("%s\n", str);
Or this is not common and has another problem?
The 2x call to snprintf() is a common idiom.
... has another problem?
Problems lie in the corners
Format maintenance
The below suffers from a repeated format - prone to breaking as code ages and and only one line is changed.
// Oops!
int need_space = snprintf(NULL, 0, "abs %s", "fgh") + 1;
char *const str = malloc(need_space * sizeof(char));
int written = snprintf(str, need_space, "abs %s ", "fgh");
Did you notice the difference?
Better to state the format once.
#define FMT_ABS_S "abs %s"
int need_space = snprintf(NULL, 0, FMT_ABS_S, "fgh");
char *const str = malloc(sizeof *str * (need_space + 1u));
int written = snprintf(str, need_space, FMT_ABS_S, "fgh");
Volatility
Code needs to insure the values do not change (non-volatile) between the 2 calls and special concern arise in multi-threaded applications.
Error checking
Code lacked checks. Yet even with checks, how to even handle unexpected results - perhaps just bail?
static const char *fmt_s = "abs %s";
int needed_space = snprintf(NULL, 0, fmt_s, "fgh");
if (needed_space < 0) {
Handle_EncodingError();
}
char * const str = malloc(sizeof *str * (needed_space + 1u));
if (str == NULL) {
Handle_OutOfMemory();
}
int written = snprintf(str, needed_space, fmt_s, "fgh");
if (written < 0 || written > needed_space) {
Handle_Error();
}
Going twice though
2 calls can be wasteful in select situations, yet commonly the impact is less than thought. #Jonathan Leffler
Yet for me, the "what to do if allocation fails" is a show stopper anyways and code reports the error and maybe exit.
Selectively, once is enough
When the s*printf() format is tightly controlled, I see 1 call as sufficient.
// int to string
#define LOG2_N 28
#define LOG2_D 93
// Number of char needed for a string of INT_MIN is log10(bit width) + 3
#define INT_SIZE ((sizeof(int)*CHAR_BIT-1)*LOG2_N/LOG2_D + 3)
char s[INT_SIZE * 2]; // I like to use 2x to handle locale issues, no need to be stingy here.
int len = snprintf(s, sizeof s, "%d", i);
if (len < 0 || (unsigned) len >= sizeof s) {
Handle_VeryUnusualFailure();
}
Related
My problem is as follows. I have to create a C program that concatenates all the parameters in input in one buffer and then this return this buffer. My solution works, but there are memory management issues.
How can I fix the problem?
#define RIALLOCA(buf, newsize) buf = realloc(buf, newsize);
char *mystrcat(char *buf, size_t sz, char *first, ...) {
va_list l;
va_start(l, first);
buf = malloc(strlen(buf) + 1);
if (sz < strlen(first) + 1) {
sz += (strlen(first) + 1);
}
RIALLOCA(buf, sz + 1 + 16);
strncat(buf, first, strlen(first));
char *nextString = va_arg(l, char *);
while (nextString != NULL) {
// sz += strlen(nextString);
RIALLOCA(buf, strlen(buf) + strlen(nextString) + 1 + 16);
strncat(buf, nextString, strlen(nextString));
nextString = va_arg(l, char *);
}
va_end(l);
return buf;
}
int main(int argc, char *argv[]) {
if (argc != 7) {
printf("troppi pochi argomenti\n");
return -1;
}
char *buffer = NULL;
RIALLOCA(buffer, 16); // macro che effettua l'allocazione
buffer[0] = '\0';
buffer = mystrcat(buffer, 16, argv[1], argv[2], argv[3], argv[4], argv[5],
argv[6], NULL);
printf("%s\n", buffer);
free(buffer);
return 0;
}
This is problem with Valgrind
The problem is that - when you call buf = malloc() inside mystrcat() - you are causing a memory leak. Memory had already been dynamically allocated for buf within your main().
Change
buf = malloc(strlen(buf)+1);
to
buf = realloc(buf, strlen(buf) + 1);
Also ... I agree with user3629249: providing macros for standard library functions in the way you have done seems redundant - at least in this context.
in function: mystrcat()
This statement:
buf = malloc(strlen(buf) + 1);
is not correct. Because buf is already a pointer to allocated memory AND this is not modifying the pointer buf back in the main() function but rather just the parameter on the stack.
To correct:
in main() (notice the additional '&' on parameter: buffer)
buffer = mystrcat(&buffer, 16, argv[1], argv[2], argv[3], argv[4],
argv[5], argv[6], NULL);
in mystrcat() remove this line
buf = malloc(strlen(buf) + 1);
regarding:
if(sz < strlen(first) + 1)
{
sz += (strlen(first) + 1);
}
since sz contains 16 and the first character in buf is '\0' This if() statement will never be entered.
Since the passed parameter buf is now a pointer to a pointer, due to the change in main(), the signature should be:
char* mystrcat(char **buf, size_t sz, char *first, ...)
and all references to buf should be de-referencing that parameter to access the pointer value in main()
There is plenty more, but the above are the underlying problems
I need the string "on" to be replaced with "in", strstr() function returns a pointer to a string so i figured assigning the new value to that pointer would work but it didn't
#include <stdio.h>
#include <string.h>
int main(void) {
char *m = "cat on couch";
*strstr(m, "on") = "in";
printf("%s\n", m);
}
Replacing a substring with another is easy if both substrings have the same length:
locate the position of the substring with strstr
if it is present, use memcpy to overwrite it with the new substring.
assigning the pointer with *strstr(m, "on") = "in"; is incorrect and should generate a compiler warning. You would avoid such mistakes with gcc -Wall -Werror.
note however that you cannot modify a string literal, you need to define an initialized array of char so you can modify it.
Here is a corrected version:
#include <stdio.h>
#include <string.h>
int main(void) {
char m[] = "cat on couch";
char *p = strstr(m, "on");
if (p != NULL) {
memcpy(p, "in", 2);
}
printf("%s\n", m);
return 0;
}
If the replacement is shorter, the code is a little more complicated:
#include <stdio.h>
#include <string.h>
int main(void) {
char m[] = "cat is out roaming";
char *p = strstr(m, "out");
if (p != NULL) {
memcpy(p, "in", 2);
memmove(p + 2, p + 3, strlen(p + 3) + 1);
}
printf("%s\n", m);
return 0;
}
In the generic case, it is even more complicated and the array must be large enough to accommodate for the length difference:
#include <stdio.h>
#include <string.h>
int main(void) {
char m[30] = "cat is inside the barn";
char *p = strstr(m, "inside");
if (p != NULL) {
memmove(p + 7, p + 6, strlen(p + 6) + 1);
memcpy(p, "outside", 7);
}
printf("%s\n", m);
return 0;
}
Here is a generic function that handles all cases:
#include <stdio.h>
#include <string.h>
char *strreplace(char *s, const char *s1, const char *s2) {
char *p = strstr(s, s1);
if (p != NULL) {
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
if (len1 != len2)
memmove(p + len2, p + len1, strlen(p + len1) + 1);
memcpy(p, s2, len2);
}
return s;
}
int main(void) {
char m[30] = "cat is inside the barn";
printf("%s\n", m);
printf("%s\n", strreplace(m, "inside", "in"));
printf("%s\n", strreplace(m, "in", "on"));
printf("%s\n", strreplace(m, "on", "outside"));
return 0;
}
There are a few problems with this approach. First, off, m is pointing to read-only memory, so attempting to overwrite the memory there it is undefined behavior.
Second, the line: strstr(m, "on") = "in" is not going to change the pointed-to string, but instead reassign the pointer.
Solution:
#include <stdio.h>
#include <string.h>
int main(void)
{
char m[] = "cat on couch";
memcpy(strstr(m, "on"), "in", 2);
printf("%s\n", m);
}
Note that if you had just used plain strcpy it would null-terminate after "cat in", so memcpy is necessary here. strncpy will also work, but you should read this discussion before using it.
It should also be known that if you are dealing with strings that are not hard-coded constants in your program, you should always check the return value of strstr, strchr, and related functions for NULL.
This function performs a generic pattern replace for all instances of a substring with a replacement string. It allocates a buffer of the correct size for the result. Behaviour is well defined for the case of the empty substring corresponding to the javascript replace() semantics. Where possible memcpy is used in place of strcpy.
/*
* strsub : substring and replace substring in strings.
*
* Function to replace a substring with a replacement string. Returns a
* buffer of the correct size containing the input string with all instances
* of the substring replaced by the replacement string.
*
* If the substring is empty the replace string is written before each character
* and at the end of the string.
*
* Returns NULL on error after setting the error number.
*
*/
char * strsub (char *input, char *substring, char *replace)
{
int number_of_matches = 0;
size_t substring_size = strlen(substring), replace_size = strlen(replace), buffer_size;
char *buffer, *bp, *ip;
/*
* Count the number of non overlapping substring occurences in the input string. This
* information is used to calculate the correct buffer size.
*/
if (substring_size)
{
ip = strstr(input, substring);
while (ip != NULL)
{
number_of_matches++;
ip = strstr(ip+substring_size, substring);
}
}
else
number_of_matches = strlen (input) + 1;
/*
* Allocate a buffer of the correct size for the output.
*/
buffer_size = strlen(input) + number_of_matches*(replace_size - substring_size) + 1;
if ((buffer = ((char *) malloc(buffer_size))) == NULL)
{
errno=ENOMEM;
return NULL;
}
/*
* Rescan the string replacing each occurence of a match with the replacement string.
* Take care to copy buffer content between matches or in the case of an empty find
* string one character.
*/
bp = buffer;
ip = strstr(input, substring);
while ((ip != NULL) && (*input != '\0'))
{
if (ip == input)
{
memcpy (bp, replace, replace_size+1);
bp += replace_size;
if (substring_size)
input += substring_size;
else
*(bp++) = *(input++);
ip = strstr(input, substring);
}
else
while (input != ip)
*(bp++) = *(input++);
}
/*
* Write any remaining suffix to the buffer, or in the case of an empty find string
* append the replacement pattern.
*/
if (substring_size)
strcpy (bp, input);
else
memcpy (bp, replace, replace_size+1);
return buffer;
}
For testing purposes I include a main program that uses the replacement function.
#define BUFSIZE 1024
char * read_string (const char * prompt)
{
char *buf, *bp;
if ((buf=(char *)malloc(BUFSIZE))==NULL)
{
error (0, ENOMEM, "Memory allocation failure in read_string");
return NULL;
}
else
bp=buf;
printf ("%s\n> ", prompt);
while ((*bp=getchar()) != '\n')bp++;
*bp = '\0';
return buf;
}
int main ()
{
char * input_string = read_string ("Please enter the input string");
char * pattern_string = read_string ("Please enter the test string");
char * replace_string = read_string ("Please enter the replacement string");
char * output_string = strsub (input_string, pattern_string, replace_string);
printf ("Result :\n> %s\n", output_string);
free (input_string);
free (pattern_string);
free (replace_string);
free (output_string);
exit(0);
}
I want to parse a string in a format as (6,8), and I want to store 6 and 8 in different variable. I'm trying to use the "strtok". However it gives me a segfault
here is my code
int main()
{
char a[80]="(6,8)";
parsing(a);
return 0;
}
int parsing(char* a)
{
char* word;
char temp[80] ;
stpcpy(temp,a);
char* new;
char **newendptr=NULL;
char **newnewendptr=NULL;
int u,v;
word= strtok(temp, "(");
word= strtok(NULL, ",");
u=strtoumax(secword,newendptr,0);
printf("%d\n",u);
new= strtok(NULL, ")");
printf("%s\n",new);
v=strtoumax(new,newnewendptr,0);
printf("%d %d",u,v);
return 1;
}
It pays to read the specification of strtok() carefully.
This code works:
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
static int parsing(char *a)
{
char temp[80];
strcpy(temp, a);
printf("temp 0 = %p [[%s]]\n", (void *)temp, temp);
char *word1 = strtok(temp, "(,)");
printf("word 1 = %p [[%s]]\n", (void *)word1, word1 == 0 ? "<nothing>" : word1);
char *word2 = strtok(NULL, "(,)");
printf("word 2 = %p [[%s]]\n", (void *)word2, word2 == 0 ? "<nothing>" : word2);
int u = strtoumax(word1, 0, 0);
int v = strtoumax(word2, 0, 0);
printf("%d %d\n", u, v);
return 1;
}
int main(void)
{
char a[80] = "(6,8)";
parsing(a);
return 0;
}
The output on my machine is:
temp 0 = 0x7fff54844440 [[(6,8)]]
word 1 = 0x7fff54844441 [[6]]
word 2 = 0x7fff54844443 [[8]]
6 8
The problem is that the call to strtok() in the original with "(" as delimiter skips over the opening (, but then doesn't find another to mark the end of the token, so the rest of the string is consumed. The second call to strtok() therefore has nothing to process and returns NULL.
The fixed code avoids that problem. The initial delimiter must include ( to skip that, and must include , to stop there. The second delimiter should include ); the other characters are not strictly needed.
Since you aren't inspecting the output pointer that's the second argument to strtoumax(), it may as well be NULL (aka 0) each time. Using strtoumax() and assigning the result to an int is a little odd. With the given data, it is OK, but more generally, it risks losing important information. The strtoX() functions are quite powerful but also remarkably delicate in how they report out of bounds values, etc. This usage throws away all that information (and you'd need to set errno to 0 before calling it, and you'd have to save the values in uintmax_t variables, to get and preserve the information accurately).
In this context, a more succinct (but not necessarily simpler) way to parse the input string would be:
char c;
if (sscanf(a, " (%d ,%d %c", &u, &v, &c) != 3 || c != ')')
…oops — malformatted data…
Make sure you know why the spaces are present and why they are where they are. That may require careful scrutiny of the POSIX specification for sscanf(). You can decide to do without the spaces; you need to know what the consequences of doing so are. If you want to be sure that the whole string was parsed, use:
char c;
int n;
if (sscanf(a, " (%d ,%d %c%n", &u, &v, &c, &n) != 3 || c != ')' || temp[n] != '\0')
…Oops…
Note that %n conversion specifications are not counted so the 3 does not change.
The answer given by #Jonathan Leffler seems to have covered what was not working in your original code nicely. I just thought that I would add a solution that will parse strings containing n-tuples of unknown length. Here, the parsing() function takes as arguments an address of a pointer to int, which is where a simulated array of numbers will be stored, and an input string, which should be formatted as "(int1, int2, ...)". I make no promises about the behavior of this function with malformed input. The function allocates space, stores the numbers in the simulated array, and returns the number of numbers found.
I included a function to display the parsed numbers, and even wrapped the call to realloc() in a function to catch allocation errors. I also added a few more examples to show how it responds to some different inputs. Of course, since my version of parsing() allocates memory, the caller is responsible for freeing it.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t parsing(int **nums, char *a);
void * xrealloc(void *ptr, size_t size);
void shownums(int *nums, size_t n);
int main(void)
{
int *nums = NULL;
size_t n;
char a[80] = "(6, 8)";
char b[80] = "(2, 3, 5)";
char c[80] = "(7)";
char d[80] = "(11,)";
char e[80] = "(2, 7, 1, 8, 2, 8, 1, 8, 2, 8, 5)";
n = parsing(&nums, a);
shownums(nums, n);
n = parsing(&nums, b);
shownums(nums, n);
n = parsing(&nums, c);
shownums(nums, n);
n = parsing(&nums, d);
shownums(nums, n);
n = parsing(&nums, e);
shownums(nums, n);
/* Free allocated memory */
free(nums);
return 0;
}
size_t parsing(int **nums, char *a)
{
size_t nums_sz = 0;
char *res;
while ((res = strtok(a, "(,)"))) {
nums_sz++;
*nums = realloc(*nums, sizeof(int) * nums_sz);
(*nums)[nums_sz - 1] = atoi(res);
a = NULL;
}
return nums_sz;
}
void * xrealloc(void *ptr, size_t size)
{
void *ret = realloc(ptr, size);
if (ret == NULL) {
fprintf(stderr, "Unable to allocate memory\n");
exit(EXIT_FAILURE);
}
return ret;
}
void shownums(int *nums, size_t n)
{
for (size_t i = 0; i < n; i++)
printf("%d ", nums[i]);
putchar('\n');
}
And here is the output:
6 8
2 3 5
7
11
2 7 1 8 2 8 1 8 2 8 5
Is there any way to allocate exactly enough space during runtime WITHOUT asking the length of the string?
int main()
{
char **tests;
int counter;
int i;
int j;
int testCases;
scanf(" %d", &testCases);
tests = malloc(sizeof(char *) * testCases);
for(i = 0; i < testCases; i++)
//stuck here, normally I would put tests[i] = malloc(length_of_string)
//but I don't know the length of string until runtime
}
Since you are using scanf to read the count, I suppose that you will be using it to read the strings as well. If your C library is Posix 2008 compatible [Note 1], then you can use the m length modifier to a scanf %s %c or %[ format, which will cause scanf to automatically allocate the string for you. (You need to supply the address of a string pointer -- i.e. char** -- instead of just a string pointer.)
Note that in the scanf format " %d", the space character is redundant. The %d format specifier, like %s, automatically skips leading whitespace.
Here's an example if you are reading white-space separated words:
int n_strings;
if (scanf("%d", &n_strings) != 1) {
/* Handle input error; do not continue */
}
/* Should check to make sure n_strings is > 0 */
char** strings = malloc(n_strings * sizeof *strings);
if (!strings) {
/* Handle alloc error; do not continue */
}
for (int i = 0; i < n_strings; ++i) {
if (scanf("%ms", &strings[i]) != 1) {
/* Handle input error, do not continue */
}
}
It's more likely that you will be wanting to read in complete lines. In that case, again using a Posix 2008 compatible library, you can use the getline function, which reads an entire line -- including the newline character -- and stores it into malloc'd storage. Unlike the scanf m modifier, getline requires that the buffer pointer whose address you give it is either NULL or the result of a previous call to malloc. Also, a successful call will return the number of characters actually stored, which can be handy.
Here's the above example using getline:
int n_strings;
if (scanf("%d", &n_strings) != 1) {
/* Handle input error; do not continue */
}
/* Skip the rest of the first line */
while (getchar() != '\n') {}
char** strings = malloc(n_strings * sizeof *strings);
if (!strings) {
/* Handle alloc error; do not continue */
}
for (char **strp = strings, **limit = strings + n_strings;
strp < limit;
++strp) {
size_t n = 0;
*strp = NULL;
ssize_t len = getline(strp, &n, stdin);
if (len <= 0) {
/* Handle input error, do not continue */
}
/* You probably don't want the trailing newline. But remember
* that is is possible that it doesn't exist if the last character
* in the file is not a newline.
*/
if (len && (*strp)[len - 1] == '\n')
(*strp)[len - 1] = 0;
}
As far as I know, the standard C library will conform to Posix 2008 if you are using reasonably modern Linux or Mac OS X. Both the features recommended here were implemented in the Gnu standard C library before being incorporated into the standard.
To enable Posix 2008 features, you will need to put one (or both) of these lines in your source code before any system include:
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700
If you are using an older glibc, in which getline and the m flag were still considered Gnu extensions, use this definition:
#define _GNU_SOURCE
Read a little bit at a time using a fixed buffer and as you go realloc the memory. You can't use scanf. It will ignore all white spaces.
#define BUFSIZE 100
#define INITIALSIZE 20
int main(int argc, char* argv[])
{
char buf[BUFSIZE];
char **tests;
int counter;
int i;
int j;
int testCases;
scanf(" %d", &testCases);
// get rid of the CR/LF
fgets( buf, sizeof(buf), stdin );
tests = (char **)malloc(sizeof(char *) * testCases);
for(i = 0; i < testCases; i++) {
int availableSpace, newSize;
availableSpace = newSize = INITIALSIZE;
tests[i] = (char *)malloc(availableSpace * sizeof(char));
tests[i][0] = '\0';
while ( fgets( buf, sizeof(buf), stdin ) != NULL )
{
if ( availableSpace <= (int) strlen(buf) ) {
newSize += (int) strlen(buf) - availableSpace + 1;
tests[i] = (char *)realloc(tests[i], newSize * sizeof(char));
availableSpace = 0;
}
else {
availableSpace -= strlen(buf);
}
strcat(tests[i], buf);
if (strlen(buf) < BUFSIZE-1) {
break;
}
}
}
}
gcc 4.4.3
c89
I have the following string
sip:12387654345443222118765#xxx.xxx.xxx.xxx
How can I extract just the number? I just want the number.
12387654345443222118765
Many thanks for any advice,
There are lots of ways to do it, if the string is well-formatted you could use strchr() to search for the : and use strchr() again to search for the # and take everything in between.
Here is another method that looks for a continuous sequence of digits:
char *start = sipStr + strcspn(sipStr, "0123456789");
int len = strspn(start, "0123456789");
char *copy = malloc(len + 1);
memcpy(copy, start, len);
copy[len] = '\0'; //add null terminator
...
//don't forget to
free(copy);
It sounds like you want it as a numeric type, which is going to be difficult (it's too large to fit in an int or a long). In theory you could just do:
const char* original = "sip:12387654345443222118765#xxx.xxx.xxx.xxx";
long num = strtoul(original + 4, NULL, 10);
but it will overflow and strtoul will return -1. If you want it as a string and you know it's always going to be that exact length, you can just pull out the substring with strcpy/strncpy:
const char* original = "sip:12387654345443222118765#xxx.xxx.xxx.xxx";
char num[24];
strncpy(num, original + 4, 23);
num[23] = 0;
If you don't know it's going to be 23 characters long every time, you'll need to find the # sign in the original string first:
unsigned int num_length = strchr(original, '#') - (original + 4);
char* num = malloc(num_length + 1);
strncpy(num, original + 4, num_length);
num[num_length] = 0;
Use a regular expression :)
#include <regex.h>
regcomp() // compile your regex
regexec() // run your regex
regfree() // free your regex
:)
Have a look into the strtok or strtok_r functions.
Here is something that will deal with a variable width substring, which doesn't care about the starting position of the substring. For instance, if string was iax2:xxx#xx.xx.xx.xx, it would still work. It will, however return NULL if either delimiter can't be found.
It uses strchr() to find the delimiters, which lets us know where to start copying and where to stop. It returns an allocated string, the calling function must free() the returned pointer.
I'm pretty sure this is what you want?
Note: Edited from original to be more re-usable and a bit saner.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *extract_string(const char *str, const char s1, const char s2)
{
char *ret = NULL, *pos1 = NULL, *pos2 = NULL;
size_t len;
if (str == NULL || s1 < 0 || s2 < 0)
return NULL;
pos1 = strchr(str, s1);
pos2 = strchr(str, s2);
if (! pos1 || ! pos2)
return NULL;
len = ((pos2 - str) - (pos1 - str) - 1);
ret = (char *) malloc(len + 1);
if (ret == NULL)
return NULL;
memcpy(ret, str + (pos1 - str) + 1, len);
ret[len] = '\0';
return ret;
}
int main(void)
{
const char *string = "sip:12387654345443222118765#xxx.xxx.xxx.xxx";
char *buff = NULL;
buff = extract_string(string, ':', '#');
if (buff == NULL)
return 1;
printf("The string extracted from %s is %s\n" , string, buff);
free(buff);
return 0;
}
You could easily modify that to not care if the second delimiter is not found and just copy everything to the right of the first. That's an exercise for the reader.