Size of formatted string - c

I am struggling to understand what happens during snprintf.
Let's say I have two numbers:
int i =11; int k = 3;
I want to format them like this "[%02d] %03d\t" and use snprintf.
Afterwards I use the resulting string with write().
snprintf needs the length/bytes n.
I do not understand what is the length I need to provide...
I have 2 theories:
a) It is
sizeof(int)*2
b) I check how many chars the formatted string will contain by counting the digits of the two integers and adding the other chars that the output will have:
2*sizeof(char) + 1*sizeof(char) + 2*sizeof(char) + 3*sizeof(char)+ 1*sizeof(char)
-> digits of i + digits of k + zeros added to first int + zeros added to second int + tab
I am struggling to understand what is the "n" I have to give to snprintf

It is the buffer size
According to a documentation:
Maximum number of bytes to be used in the buffer. The generated string
has a length of at most n-1, leaving space for the additional
terminating null character. size_t is an unsigned integral type.
Suppose you write to an array such as this:
char buf[32];
The buffer can hold 32 chars (including the null terminator). Therefore we call the function like this:
snprintf (buf, 32, "[%02d] %03d\t", i, k);
You can also check the return value to see how many chars have been written (or would have been written). In this case, if it's bigger than 32, then that would mean that some characters had to be discarded because they didn't fit.

Pass 0 and NULL first to obtain an exact amount
int n = snprintf(NULL, 0, "[%02d] %03d\t", i, k);
Then you know you need n + 1
char *buf = malloc(n + 1);
snprintf(buf, n + 1, "[%02d] %03d\t", i, k);
free(buf);
See it on ideone: https://ideone.com/pt0cOQ

n is the size of the string you're passing into snprintf, so it knows when to stop writing to the buffer. This is to prevent a category of errors knows as buffer overflows. snprintf will write n - 1 characters into the passed-in buffer and then terminate it with the null character.

Related

C - Print ASCII Value for Each Character in a String

I'm new to C and I'm trying to write a program that prints the ASCII value for every letter in a name that the user enters. I attempted to store the letters in an array and try to print each ASCII value and letter of the name separately but, for some reason, it only prints the value of the first letter.
For example, if I write "Anna" it just prints 65 and not the values for the other letters in the name. I think it has something to do with my sizeof(name)/sizeof(char) part of the for loop, because when I print it separately, it only prints out 1.
I can't figure out how to fix it:
#include <stdio.h>
int main(){
int e;
char name[] = "";
printf("Enter a name : \n");
scanf("%c",&name);
for(int i = 0; i < (sizeof(name)/sizeof(char)); i++){
e = name[i];
printf("The ASCII value of the letter %c is : %d \n",name[i],e);
}
int n = (sizeof(name)/sizeof(char));
printf("%d", n);
}
Here's a corrected, annotated version:
#include <stdio.h>
#include <string.h>
int main() {
int e;
char name[100] = ""; // Allow for up to 100 characters
printf("Enter a name : \n");
// scanf("%c", &name); // %c reads a single character
scanf("%99s", name); // Use %s to read a string! %99s to limit input size!
// for (int i = 0; i < (sizeof(name) / sizeof(char)); i++) { // sizeof(name) / sizeof(char) is a fixed value!
size_t len = strlen(name); // Use this library function to get string length
for (size_t i = 0; i < len; i++) { // Saves calculating each time!
e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
printf("\n Name length = %zu\n", strlen(name)); // Given length!
int n = (sizeof(name) / sizeof(char)); // As noted above, this will be ...
printf("%d", n); // ... a fixed value (100, as it stands).
return 0; // ALWAYS return an integer from main!
}
But also read the comments given in your question!
This is a rather long answer, feel free to skip to the end for the code example.
First of all, by initialising a char array with unspecified length, you are making that array have length 1 (it only contains the empty string). The key issue here is that arrays in C are fixed size, so name will not grow larger.
Second, the format specifier %c causes scanf to only ever read one byte. This means that even if you had made a larger array, you would only be reading one byte to it anyway.
The parameter you're giving to scanf is erroneous, but accidentally works - you're passing a pointer to an array when it expects a pointer to char. It works because the pointer to the array points at the first element of the array. Luckily this is an easy fix, an array of a type can be passed to a function expecting a pointer to that type - it is said to "decay" to a pointer. So you could just pass name instead.
As a result of these two actions, you now have a situation where name is of length 1, and you have read exactly one byte into it. The next issue is sizeof(name)/sizeof(char) - this will always equal 1 in your program. sizeof char is defined to always equal 1, so using it as a divisor causes no effect, and we already know sizeof name is equal to 1. This means your for loop will only ever read one byte from the array. For the exact same reason n is equal to 1. This is not erroneous per se, it's just probably not what you expected.
The solution to this can be done in a couple of ways, but I'll show one. First of all, you don't want to initialize name as you do, because it always creates an array of size 1. Instead you want to manually specify a larger size for the array, for instance 100 bytes (of which the last one will be dedicated to the terminating null byte).
char name[100];
/* You might want to zero out the array too by eg. using memset. It's not
necessary in this case, but arrays are allowed to contain anything unless
and until you replace their contents.
Parameters are target, byte to fill it with, and amount of bytes to fill */
memset(name, 0, sizeof(name));
Second, you don't necessarily want to use scanf at all if you're reading just a byte string from standard input instead of a more complex formatted string. You could eg. use fgets to read an entire line from standard input, though that also includes the newline character, which we'll have to strip.
/* The parameters are target to write to, bytes to write, and file to read from.
fgets writes a null terminator automatically after the string, so we will
read at most sizeof(name) - 1 bytes.
*/
fgets(name, sizeof(name), stdin);
Now you've read the name to memory. But the size of name the array hasn't changed, so if you used the rest of the code as is you would get a lot of messages saying The ASCII value of the letter is : 0. To get the meaningful length of the string, we'll use strlen.
NOTE: strlen is generally unsafe to use on arbitrary strings that might not be properly null-terminated as it will keep reading until it finds a zero byte, but we only get a portable bounds-checked version strnlen_s in C11. In this case we also know that the string is null-terminated, because fgets deals with that.
/* size_t is a large, unsigned integer type big enough to contain the
theoretical maximum size of an object, so size functions often return
size_t.
strlen counts the amount of bytes before the first null (0) byte */
size_t n = strlen(name);
Now that we have the length of the string, we can check if the last byte is the newline character, and remove it if so.
/* Assuming every line ends with a newline, we can simply zero out the last
byte if it's '\n' */
if (name[n - 1] == '\n') {
name[n - 1] = '\0';
/* The string is now 1 byte shorter, because we removed the newline.
We don't need to calculate strlen again, we can just do it manually. */
--n;
}
The loop looks quite similar, as it was mostly fine to begin with. Mostly, we want to avoid issues that can arise from comparing a signed int and an unsigned size_t, so we'll also make i be type size_t.
for (size_t i = 0; i < n; i++) {
int e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
Putting it all together, we get
#include <stdio.h>
#include <string.h>
int main() {
char name[100];
memset(name, 0, sizeof(name));
printf("Enter a name : \n");
fgets(name, sizeof(name), stdin);
size_t n = strlen(name);
if (n > 0 && name[n - 1] == '\n') {
name[n - 1] = '\0';
--n;
}
for (size_t i = 0; i < n; i++){
int e = name[i];
printf("The ASCII value of the letter %c is : %d \n", name[i], e);
}
/* To correctly print a size_t, use %zu */
printf("%zu\n", n);
/* In C99 main implicitly returns 0 if you don't add a return value
yourself, but it's a good habit to remember to return from functions. */
return 0;
}
Which should work pretty much as expected.
Additional notes:
This code should be valid C99, but I believe it's not valid C89. If you need to write to the older standard, there are several things you need to do differently. Fortunately, your compiler should warn you about those issues if you tell it which standard you want to use. C99 is probably the default these days, but older code still exists.
It's a bit inflexible to be reading strings into fixed-size buffers like this, so in a real situation you might want to have a way of dynamically increasing the size of the buffer as necessary. This will probably require you to use C's manual memory management functionality like malloc and realloc, which aren't particularly difficult but take greater care to avoid issues like memory leaks.
It's not guaranteed the strings you're reading are in any specific encoding, and C strings aren't really ideal for handling text that isn't encoded in a single-byte encoding. There is support for "wide character strings" but probably more often you'll be handling char strings containing UTF-8 where a single codepoint might be multiple bytes, and might not even represent an individual letter as such. In a more general-purpose program, you should keep this in mind.
If we need write a code to get ASCII values of all elements in a string, then we need to use "%d" instead of "%c". By doing this %d takes the corresponding ascii value of the following character.
If we need to only print the ascii value of each character in the string. Then this code will work:
#include <stdio.h>
char str[100];
int x;
int main(){
scanf("%s",str);
for(x=0;str[x]!='\0';x++){
printf("%d\n",str[x]);
}
}
To store all corresponding ASCII value of character in a new variable, we need to declare an integer variable and assign it to character. By this way the integer variable stores ascii value of character. The code is:
#include <stdio.h>
char str[100];
int x,ascii;
int main(){
scanf("%s",str);
for(x=0;str[x]!='\0';x++){
ascii=str[x];
printf("%d\n",ascii);
}
}
I hope this answer helped you.....😊

Divide a string into a certain number of substrings (C)

So, I have a certain file I read as input and load it into the memory, and obtain a cstring char* text.
I want to split that string into a certain number of strings. Say I have 4 threads, for example, and I want each thread to print a certain "piece" of the string (not necessarily in order).
int num_threads = 4;
char *filename = "file.txt";
int file_size = load_file_to_mem(filename, &text); // I made this and it works.
int text_size = strlen(text);
int substring_size; // that would be the size of each substring
char to_thread[num_threads][block_size];
How can I split the *text string into 4 same sized substrings, that is, how can I find the block size so I can make each thread receive a substring of that size?
Unless I misunderstand the question, one way to do it is with the ceil() function: num_threads - 1 substrings would each be of length ceil(test_size / num_threads) + 1. The remaining single substring would be of length test_size - (num_threads - 1) * ceil(test_size / num_threads) + 1.
You might do it this way because num_threads may not divide test_size evenly. Add one byte to include space for a null terminator character for each substring.
Dividing the string is relatively easy: you have the string size, which you obtained from strlen, and you can compute the substring size by dividing the size by 4. You could then use something like strncpy to copy just the amount of characters you need from the string you need to split into some temporary buffers, for example. Basically, for thread i you need to copy substring_size characters, starting from character substring_size * i (just use a pen and paper to see why this happens).
Now, you have 2 problems. One is that the size of the string may not be divisible by 4, which means that the strings printed from the threads will not be equal. The way you handle this is a matter of choice. One way to go would be:
base_substring_size = string_size / no_of_threads
remainder_substring_size = string_size % no_of_threads
for thread_id = 0 to no_of_threads:
this_thread_size = base_substring_size
if remainder_substring_size > 0
this_thread_size += 1
This way, you have in the this_thread_size variable the exact size of the buffer for each thread. And do not forget about the NUL terminator.

Convert array of bytes to hexadecimal string in plain old C

I am looking into a method to convert a byte array to a hexadecimal string here is what i coded :
unsigned char buffer[] = {0xAA,0xBB,0x01,0xAB,0x11,0x12,0x13,0x22,0x11,0x14};
int _tmain(int argc, _TCHAR* argv[])
{
char * asta = (char*)malloc(16);
memset(asta,0,16);
int k;
for (k = 0; k < 16 ; k++)
{
sprintf(&asta[k],"%X",buffer[4 + k]);
}
printf("%s",asta);
_getch();
}
Only the first byte is converted correctly the rest are not. How can i fix the code ?
You have 10 bytes in your array, so your buffer needs at least 21 bytes (2 hex digits are needed for each byte + 1 for the null terminator).
I don't understand what you are doing here:
sprintf(&asta[k],"%X",buffer[4 + k]);
Why are you starting with the fifth byte in the buffer? Also, each byte in your buffer takes two bytes in the string, so you need to print to asta[2 * k].
Putting it together you get something like:
char * asta = (char*)calloc(2 * sizeof buffer + 1, sizeof(char)); // calloc automatically zeros asta
int k;
for (k = 0; k < sizeof buffer ; k++)
{
sprintf(&asta[2 * k],"%02X", (unsigned int)buffer[k]); // Not sure if the cast is needed
}
printf("%s",asta);
You have to remember that two-digit hexadecimal numbers will still be two digits when you print it as a string, i.e. it will take up two characters.
In the loop, the second iteration will overwrite the second character of the string, the third iteration will overwrite the third characters, etc.
Also, since each two-digit number will use two characters, you must allocate memory for 32 characters, plus one for the string terminating '\0' character.
And as noted in the comments, you are accessing data outside of your array.

sscanf doesn't move, scanning same integer everytime

I have a string that has ints and I'm trying to get all the ints into another array. When sscanf fails to find an int I want the loop to stop. So, I did the following:
int i;
int getout = 0;
for (i = 0; i < bsize && !getout; i++) {
if (!sscanf(startbuffer, "%d", &startarray[i])) {
getout = 1;
}
}
//startbuffer is a string, startarray is an int array.
This results in having all the elements of startarray to be the first char in startbuffer.
sscanf works fine but it doesn't move onto the next int it just stays at the first position.
Any idea what's wrong? Thanks.
The same string pointer is passed each time you call sscanf. If it were to "move" the input, it would have to move all the bytes of the string each time which would be slow for long strings. Furthermore, it would be moving the bytes that weren't scanned.
Instead, you need to implement this yourself by querying it for the number of bytes consumed and the number of values read. Use that information to adjust the pointers yourself.
int nums_now, bytes_now;
int bytes_consumed = 0, nums_read = 0;
while ( ( nums_now =
sscanf( string + bytes_consumed, "%d%n", arr + nums_read, & bytes_now )
) > 0 ) {
bytes_consumed += bytes_now;
nums_read += nums_now;
}
Convert the string to a stream, then you can use fscanf to get the integers.
Try this.
http://www.gnu.org/software/libc/manual/html_node/String-Streams.html
You are correct: sscanf indeed does not "move", because there is nothing to move. If you need to scan a bunch of ints, you can use strtol - it tells you how much it read, so you can feed the next pointer back to the function on the next iteration.
char str[] = "10 21 32 43 54";
char *p = str;
int i;
for (i = 0 ; i != 5 ; i++) {
int n = strtol(p, &p, 10);
printf("%d\n", n);
}
This is the correct behavior of sscanf. sscanf operates on a const char*, not an input stream from a file, so it will not store any information about what it has consumed.
As for the solution, you can use %n in the format string to obtain the number of characters that it has consumed so far (this is defined in C89 standard).
e.g. sscanf("This is a string", "%10s%10s%n", tok1, tok2, &numChar); numChar will contain the number of characters consumed so far. You can use this as an offset to continue scanning the string.
If the string only contains integers that doesn't exceed the maximum value of long type (or long long type), use strtol or strtoll. Beware that long type can be 32-bit or 64-bit, depending on the system.

Why is the argument to malloc i+1 in this C code?

Usually a malloc contains sizeof , but this one doesn't and has i+1 instead:
int main ()
{
int i,n;
char * buffer;
printf ("How long do you want the string? ");
scanf ("%d", &i);
buffer = (char*) malloc (i+1);
if (buffer==NULL) exit (1);
If you wanted to allocate an array of some type, you would normally multiply the number of elements you wanted by the size of that type, because malloc takes the size of the array in bytes.
However, an array of char is a special case; you do not need to multiply the number of elements you want by sizeof(char), because sizeof(char) is defined by the Standard to always be 1, and multiplication by 1 yields the other operand.
The + 1 is to make room for the NUL terminator. If you want a string of length n, your array has to be of length n + 1; n spaces for the n characters of the string, and 1 space for the terminator.
By the way, you shouldn't cast the return value of malloc. It will make your code easier to change in the future.
C strings are null-terminated, see:
http://en.wikipedia.org/wiki/Null-terminated_string
E.g. "abc" is represented as "abc" + NUL. Thus, for a string of length i, you need to allocate i + 1. As you are allocating chars, sizeof(char) is 1, so you actually need to allocate 1 * (i + 1), but that's the same.
If its allocating for a string you need enough memory for the number of characters + 1 extra for the null character that denotes the end of the string (\0)

Resources