First, a C string is not just a char, but an array of char with the last element (or at least the last one that's counted as part of the string) set to the null character (numerically 0, also '\0' as a character constant).
Next, in the code you posted you probably meant char buffer[50] rather than char *buffer[50]... the version you have is an array of 50 char *s, but you need an array of 50 chars. After that's corrected, then...
Since fgets() always fills in a null char at the end of the string it read, buffer would already be a valid C string after you call fgets(). If you'd like to copy it to another string so you can reuse the buffer to read more input, you can use the usual string handling functions from <string.h>, such as strcpy(). Just make sure the string you copy it into is large enough to hold all the used characters plus a terminating null character.
This code copies the string into a newly malloc()ed string (error checking omitted):
char buffer[50];
char *str;
fgets(buffer,50,stdin);
str = malloc(strlen(buffer) + 1);
strcpy(str,buffer);
This code does the same, but copies to a char array on the stack (not malloc()ed):
char buffer[50];
char str[50];
fgets(buffer,50,stdin);
strcpy(str,buffer);
strlen() will tell you how many characters are used in the string, but doesn't count the terminating null (so you need to have one more character allocated than what strlen() returns). strcpy() will copy the characters and the null at the end from one string/buffer to another. It stops after the null, and doesn't know how much space you've allocated -- so you need to make sure it will find a null character before running out of space in the destination, or reaching the end of the source buffer. If in doubt, place a null at the end of the buffer yourself to make sure.
I'm a beginner and lost between all these print functions. I already managed to copy a 4byte string to a 4 byte buffer using snprintf(). Is there a similar function to unpack it back to a string on the other side?
First, a C string is not just a char, but an array of char with the last element (or at least the last one that's counted as part of the string) set to the null character (numerically 0, also '\0' as a character constant).
Next, in the code you posted you probably meant char buffer[50] rather than char *buffer[50]... the version you have is an array of 50 char *s, but you need an array of 50 chars. After that's corrected, then...
Since fgets() always fills in a null char at the end of the string it read, buffer would already be a valid C string after you call fgets(). If you'd like to copy it to another string so you can reuse the buffer to read more input, you can use the usual string handling functions from <string.h>, such as strcpy(). Just make sure the string you copy it into is large enough to hold all the used characters plus a terminating null character.
This code copies the string into a newly malloc()ed string (error checking omitted):
char buffer[50];
char *str;
fgets(buffer,50,stdin);
str = malloc(strlen(buffer) + 1);
strcpy(str,buffer);
This code does the same, but copies to a char array on the stack (not malloc()ed):
char buffer[50];
char str[50];
fgets(buffer,50,stdin);
strcpy(str,buffer);
strlen() will tell you how many characters are used in the string, but doesn't count the terminating null (so you need to have one more character allocated than what strlen() returns). strcpy() will copy the characters and the null at the end from one string/buffer to another. It stops after the null, and doesn't know how much space you've allocated -- so you need to make sure it will find a null character before running out of space in the destination, or reaching the end of the source buffer. If in doubt, place a null at the end of the buffer yourself to make sure.
It should be char buffer[50]; and yes, you can then use strncpy (which does not care if it got a static or a heap allocated zone).
But I would recommend using getline in your case.
c - Converting buffer to array of strings - Stack Overflow
c - copying strings into a buffer - Stack Overflow
How do I convert a buffer to a binary string in c++? - Stack Overflow
How do you convert a byte array to a hexadecimal string in C? - Stack Overflow
The problem is in this line
for (int i = 0; i < Length, x<500; i++)
Your condition is wrong, it should be:
for (int i = 0; i < Length && x<500; i++)
Also, the else if block is never executed, because '%' is not equal to '\0'. This can be fixed by swapping them.
if(data[i] == '%')
{
strcpy(set[x], record);
x++;
y = 0;
}
else if(data[i] != '\0')
{
record[y] = data[i];
y++;
}
The third problem is that the last word in your %-delimited string will not be copied into set since there is no percent sign after it.
There is one more bug. You forgot to put a null-terminator at the end of record before copying it, this causes shorter strings to retain letters from previous ones.
record[y] = '\0';
strcpy(set[x], record);
At this point I recommend using strtok from <string.h>, and a memory safe programming language like Rust.
you can do like this
char** make_array(_In_ char* buf, _Out_ unsigned* pn)
{
char* pc = buf;
unsigned n = 1;
while(pc = strchr(pc, '%')) n++, *pc++ = 0;
if (char** arr = new char*[n])
{
*pn = n;
char** ppc = arr;
do {
*ppc++ = buf;
buf += strlen(buf) + 1;
} while(--n);
return arr;
}
*pn = 0;
return 0;
}
void demo()
{
char buf[] = "1111%2222%33333";
unsigned n;
if (char** arr = make_array(buf, &n))
{
char** ppc = arr;
do {
printf("%s\n", *ppc++);
} while (--n);
delete [] arr;
}
}
The array animals is an array of pointers. It is not an array of buffers of some size. Therefor, if you do
sizeof(*animals)
You will get the sizeof of the first element of that array. Equivalent to
sizeof(char*)
Because your array stores pointers. So, in the line that reads
char *output[sizeof(*animals)];
You allocate 4 or 8 pointers in one array (depends on how wide a pointer on your platform is. Usually it's either 4 or 8). But that's of course not senseful! What you wanted to do is create an array of pointers of the same size as animals. You will have to first get the total size of the animals array, and then divide by the size of one element
char *output[sizeof(animals)/sizeof(*animals)];
Now, that is what you want. But the pointers will yet have indeterminate values... Next you pass the array using *&animals (same for the other). Why that? You can pass animals directly. Taking its address and then dereference is the same as doing nothing in the first place.
Then in the function you call, you copy the strings pointed to by elements in animal to some indeterminate destination (remember the elements of the output array - the pointers - have yet indeterminate values. We have not assigned them yet!). You first have to allocate the right amount of memory and make the elements point to that.
while(*animals) {
// now after this line, the pointer points to something sensible
*output = malloc(sizeof("new animal ") + strlen(*animals));
sprintf(*output, "new animal %s", *animals);
output++; // no need to dereference the result
animals++; // don't forget to increment animals too!
}
Addition, about the sizeof above
There's one important thing you have to be sure about. It's the way we calculate the size. Whatever you do, make sure you always have enough room for your string! A C string consists of characters and a terminating null character, which marks the end of the string. So, *output should point to a buffer that is at least as large so that it contains space for "new animal " and *animals. The first contains 11 characters. The second depends on what we actually copy over - its length is what strlen returns. So, in total we need
12 + strlen(*animals)
space for all characters including the terminating null. Now it's not good style to hardcode that number into your code. The prefix could change and you could forget to update the number or miscount about one or two characters. That is why we use sizeof, which we provide with the string literal we want to have prepended. Recall that a sizeof expression evaluates to the size of its operand. You use it in main to get the total size of your array before. Now you use it for the string literal. All string literals are arrays of characters. string literals consist of the characters you type in addition to the null character. So, the following condition holds, because strlen counts the length of a C string, and does not include the terminating null character to its length
// "abc" would have the type char[4] (array of 4 characters)
sizeof "..." == strlen("...") + 1
We don't have to divide by the size of one element, because the sizeof char is one anyway, so it won't make a difference. Why do we use sizeof instead of strlen? Because it already accounts for the terminating null character, and it evaluates at compile time. The compiler can literally substitute the size that the sizeof expression returns.
You haven't allocated any space in your output array to put the copy into. You'll need to use malloc to allocate some space before using sprintf to copy into that buffer.
void p_init(const char **animals, char **output)
{
while(*animals)
{
size_t stringSize = 42; /* Use strlen etc to calculate the size you need, and don't for get space for the NULL! */
*output = (char *)malloc(stringSize);
sprintf(*output, "new animal %s", *animals);
output++;
animals++;
}
}
Don't forget to call free() on that allocated memory when you are done with it.
The array bitstring has what is known as automatic duration, which means that it springs into existence when the function is called and disappears when the function returns.
Therefore, the pointer that this version of getBufferAsBinaryString returns is to an array which no longer exists by the time the caller receives the pointer. (Remember that the statement return bitstring; returns a pointer to the first character in bitstring; by the "equivalence of arrays and pointers," the mention of the array bitstring in this context is equivalent to &bitstring[0].)
When the caller tries to use the pointer, the string created by getBufferAsBinaryString might still be there, or the memory might have been re-used by some other function. Therefore, this version of getBufferAsBinaryString is not adequate and not acceptable. Functions must never return pointers to local, automatic-duration arrays.
Since the problem with returning a pointer to a local array is that the array has automatic duration by default, the simplest fix to the above non-functional version of getBufferAsBinaryString is to declare the array static, instead:
char *getBufferAsBinaryString(void *in)
{
int pos=0;
char result;
static char bitstring[256];
memset(bitstring, 0, 256);
unsigned int *input= (unsigned int *)in;
for(int i=31;i>=0;i--)
{
if (((*input >> i) & 1)) result = '1';
else result = '0';
bitstring[pos] = result;
if ((i>0) && ((i)%4)==0)
{
pos++;
bitstring[pos] = ' ';
}
pos++;
}
return bitstring;
}
Now, the bitstring array does not disappear when getBufferAsBinaryString returns, so the pointer is still valid by the time the caller uses it.
Returning a pointer to a static array is a practical and popular solution to the problem of "returning" an array, but it has one drawback. Each time you call the function, it re-uses the same array and returns the same pointer. Therefore, when you call the function a second time, whatever information it "returned" to you last time will be overwritten. (More precisely, the information, that the function returned a pointer to, will be overwritten.)
Although the static return array technique will work, the caller has to be a little bit careful, and must never expect the return pointer from one call to the function to be usable after a later call to the function
But you still have a different problem with passing in void * which I did not cover, in addition not passing in the size of your buffer. Since you shouldn't assume that your array ends in the \0, you should also pass in the size of your buffer.
Ignoring the other issues with memory and safety, you are not returning a valid null terminated string. Nor do you pass in the size of the input buffer, so you really just print out the 32-bit bit representation of the first 32-bits of the input buffer.
So, in addition to passing in a char buffer to write to or simply returning a std::string, you should also pass in the size of the input buffer and loop over that as well.
printf("%02X:%02X:%02X:%02X", buf[0], buf[1], buf[2], buf[3]);
For a more generic way:
int i;
for (i = 0; i < x; i++)
{
if (i > 0) printf(":");
printf("%02X", buf[i]);
}
printf("\n");
To concatenate to a string, there are a few ways you can do this. I'd probably keep a pointer to the end of the string and use sprintf. You should also keep track of the size of the array to make sure it doesn't get larger than the space allocated:
int i;
char* buf2 = stringbuf;
char* endofbuf = stringbuf + sizeof(stringbuf);
for (i = 0; i < x; i++)
{
/* i use 5 here since we are going to add at most
3 chars, need a space for the end '\n' and need
a null terminator */
if (buf2 + 5 < endofbuf)
{
if (i > 0)
{
buf2 += sprintf(buf2, ":");
}
buf2 += sprintf(buf2, "%02X", buf[i]);
}
}
buf2 += sprintf(buf2, "\n");
For completude, you can also easily do it without calling any heavy library function (no snprintf, no strcat, not even memcpy). It can be useful, say if you are programming some microcontroller or OS kernel where libc is not available.
Nothing really fancy you can find similar code around if you google for it. Really it's not much more complicated than calling snprintf and much faster.
#include <stdio.h>
int main(){
unsigned char buf[] = {0, 1, 10, 11};
/* target buffer should be large enough */
char str[12];
unsigned char * pin = buf;
const char * hex = "0123456789ABCDEF";
char * pout = str;
int i = 0;
for(; i < sizeof(buf)-1; ++i){
*pout++ = hex[(*pin>>4)&0xF];
*pout++ = hex[(*pin++)&0xF];
*pout++ = ':';
}
*pout++ = hex[(*pin>>4)&0xF];
*pout++ = hex[(*pin)&0xF];
*pout = 0;
printf("%s\n", str);
}
Here is another slightly shorter version. It merely avoid intermediate index variable i and duplicating laste case code (but the terminating character is written two times).
#include <stdio.h>
int main(){
unsigned char buf[] = {0, 1, 10, 11};
/* target buffer should be large enough */
char str[12];
unsigned char * pin = buf;
const char * hex = "0123456789ABCDEF";
char * pout = str;
for(; pin < buf+sizeof(buf); pout+=3, pin++){
pout[0] = hex[(*pin>>4) & 0xF];
pout[1] = hex[ *pin & 0xF];
pout[2] = ':';
}
pout[-1] = 0;
printf("%s\n", str);
}
Below is yet another version to answer to a comment saying I used a "trick" to know the size of the input buffer. Actually it's not a trick but a necessary input knowledge (you need to know the size of the data that you are converting). I made this clearer by extracting the conversion code to a separate function. I also added boundary check code for target buffer, which is not really necessary if we know what we are doing.
#include <stdio.h>
void tohex(unsigned char * in, size_t insz, char * out, size_t outsz)
{
unsigned char * pin = in;
const char * hex = "0123456789ABCDEF";
char * pout = out;
for(; pin < in+insz; pout +=3, pin++){
pout[0] = hex[(*pin>>4) & 0xF];
pout[1] = hex[ *pin & 0xF];
pout[2] = ':';
if (pout + 3 - out > outsz){
/* Better to truncate output string than overflow buffer */
/* it would be still better to either return a status */
/* or ensure the target buffer is large enough and it never happen */
break;
}
}
pout[-1] = 0;
}
int main(){
enum {insz = 4, outsz = 3*insz};
unsigned char buf[] = {0, 1, 10, 11};
char str[outsz];
tohex(buf, insz, str, outsz);
printf("%s\n", str);
}
Given your input strings are not null terminated, you shouldn't use str... functions. You also can't use the popularly used std::string constructors. However, you can use this constructor:
std::string str(buffer, buflen): it takes a char* and a length. (actually const char* and length)
I would avoid the C string version. This would give:
std::string bufferToString(char* buffer, int bufflen)
{
std::string ret(buffer, bufflen);
return ret;
}
If you really must use the C-string version, either drop a 0 at the bufflen position (if you can) or create a buffer of bufflen+1, then memcpy the buffer into it, and drop a 0 at the end (bufflen position).
If the data buffer may have null ('\0') characters in it, you don't want to use the null-terminated operations.
You can either use the constructor that takes char*, length.
char buff[4] = {'a', 'b', 'c', 'd'};
cout << std::string(&buff[0], 4);
Or you can use the constructor that takes a range:
cout << std::string(&buff[0], &buff[4]); // end is last plus one
Do NOT use the std::string(buff) constructor with the buff[] array above, because it is not null-terminated.
In addition to what the other answers say and I generally agree with:
Don't call strlen more than once. That's just a waste. Count the length of orig_str and append_str once at the beginning of the function and store them away in const variables. That will also make your initial check whether append_str is empty look cleaner:
const size_t append_len = strlen(append_str);
if (!append_len)
return orig_str;
const size_t orig_len = strlen(orig_str);
Moving the strlen(orig_str) above the conditional would look cleaner in my eyes but it might be a pessimization if append_str is often empty.
Of course, the most efficient way would be if you could pass orig_len as an additional input parameter. Or you could use a
struct string_buffer
{
size_t length;
size_t capacity;
char * data;
};
that encapsulates all the required info and change your function's signature to
ssize_t
append_string(struct string_buffer * buffer, const char * appendix);
where the function could return the number of bytes successfully appended or –1 on error.
My second remark would be that you are growing your buffer by a constant amount each time you reallocate. It should really grow exponentially unless you really know that linear growth will be preferable in your specific use-case, which I find hard to imagine how that could be.
Concerning your comments: there is no NULL byte. NULL is a macro constant for the null pointer. The null byte is abbreviated as NUL in the ASCII character set. I would also find it helpful to have your function's semantics exactly documented in a comment. For example, should the user free the buffer if it fails and returns NULL? Will the existing data be modified in that case? I can try answering those questions by reading your implementation but that doesn't allow me to distinguish a bug in your code from a different valid design decision.
strcat(orig_str, append_str)looks cleaner than astrcpy/strlencombo.memsetis pointless. You need only 1 terminating zero, and it is taken care of by string routines.The responsibilities of
if (req_space > *orig_str_size)and a correspondingelseclauses overlap. The only task of theifclause should be string reallocation. Copying needs to be performed in any case. Consider insteadif (req_space > *orig_str_size) { allocate = ...; char *new_str = realloc(orig_str, allocate); if (new_str == NULL) { handle_error; } *orig_str_size = allocate; orig_str = new_str; } strcat(orig_str, append_str);fprintfdoesn't belong to a such low level routine. Returning an error condition is enough. It is up to the caller to print or not to print.
You must check the return value of scanf. If you don't, you run into an endless loop when reaching the end of the file. And, even worse, you invoke undefined behavior.
You could split the loop into two: first, skip all whitespace, then read the remaining text.
int dscan(char *buf, int bufsize) {
int ch;
while ((ch = getc()) != EOF && ch != '\n' && isspace(ch)) {
continue;
}
int idx = 0;
while (ch != EOF && ch != '\n' && idx < bufsize) {
buf[idx++] = ch;
ch = getc();
}
if (ch == '\n' && idx < bufsize) {
buf[idx++] = '\n';
}
if (idx < bufsize) {
buf[idx++] = '\0';
}
return idx;
}
I changed several things:
- EOF is handled correctly. On EOF, the string is returned, properly null-terminated. (This was not the case in your code.)
- Leading whitespace does not count towards the buffer, it is simply ignored, like you said in your description. Your code counted it.
- Each call to an I/O function is checked for an error result.
- It's easy to strip the newline character from the string by removing the corresponding
ifsentence. Depending on whether the calling code needs this or not. - If
dscan(buf, size) < size, the buffer is a string, i.e. it is properly null-terminated. - Calling
isspacedoes not invoke undefined behavior. In your code, if I enter a negative char, the behavior is undefined. See the<ctype.h>documentation.
what can be improved?
The contract "scan input to string buffer" and "a function that reads standard input, ignores initial whitespace and fills the buffer with that data." lacks directions on how to handle many cases.
Code appears to be reading until end-of-line, yet that is not specified. Even if so, with ignoring leading white-space, it makes sense to also ignore trailing-white-space including the potential
'\n'.Appending a
'\0'to insure a string does meet the title requirement. Should anything special happen if a null character'\0'is read?Buffer size too small? What to do with extra characters? Consume them, leave them for the next I/O functions?
How to handle an immediate end-of-file? Or an end-of-file after some characters?
How to handle a rare input error?
How to handle pathological input like
where == NULL,size <= 0?Array sizes exceeding
INT_MAX?
Perhaps some of these corner case result in a "don't care" or UB as the contract does not specify a behavior. Yet consider a production task with the loose requirements.
Customers expect code to behave well is "all" situations. Although every conceivable issue is a worthy goal and not truly attainable, as an implementor, code should be resilient to many situations. These include ones that are not considered by the customer, yet reasonably possible. For corner cases that significantly reduce performance, greater coding goal detail is needed.
@Roland Illig addresses some coding weakness in OP's code. Some additional ideas:
// Add functional comment
// Return 0 when no data read or error
// int dscan(char *where, int size) {
// Use size_t in and out to convey buffer size and usage
size_t dscan(char *where, size_t size) {
// Assess input values
if (where == NULL || size == 0) {
return 0;
}
// decrement to save space for a \0
size--;
// Read the entire line
// getchar() better than scanf("%c", &c)
// It gets an unsigned char value and indicates end-of-file/error
int c;
size_t i = 0;
size_t non_white_space_next = 0;
while ((c = fgetc(stdin)) != EOF && c != '\n') {
if (i < size) {
if (isspace(c)) {
if (i == 0) continue;
where[i++] = c;
} else {
where[i++] = c;
non_white_space_next = i;
}
} else {
; // What to do with too many characters?
// For now, code will ignore them
// Yet code should convey that buffer was too small.
}
}
// If code is to ignore trailing space
i = non_white_space_next;
// Always append a null character
where[i] = '\0';
// Typically if no characters are read or an error occur,
// code should return 0 to indicate
if (c == EOF && (ferror(stdin) || i == 0)) {
return 0;
}
return i+1;
}
Assuming that
- by “string” you mean a null-terminated string as is normally meant in C;
- you haven't yet allocated memory in
data; - you already know that s <= x
First you need to allocate memory in data. Don't forget the room for the 0 byte at the end of the string.
data = malloc(s+1);
if (data == NULL) {
... /*out-of-memory handler*/
}
Assuming malloc succeeds, you can now copy the bytes.
EDIT:
The best function for the job, as pointed out by caf, is strncat. (It's fully portable, being part of C89.) It appends to the destination string, so arrange for the destination to be an empty string beforehand:
*(char*)data = 0;
strncat(data, buf, s);
Other inferior possibilities, kept here to serve as examples of related functions:
If you have
strlcpy(which is not standard C but is common on modern Unix systems; there are public domain implementations floating around):strlcpy(data, buf, s+1);If you know that there are at least
scharacters in the source string, you can usememcpy:memcpy(data, buf, s);((char*)data)[s+1] = 0;
Otherwise you can compute the length of the source string first:
size_t bytes_to_copy = strlen(buf); if (bytes_to_copy > s) bytes_to_copy = s; memcpy(data, buf, bytes_to_copy); ((char*)data)[s+1] = 0;Or you can use
strncpy, though it's inefficient if the actual length of the source string is much smaller thans:strncpy(data, buf, s); ((char*)data)[s+1] = 0;
If data is not allocated:
char buf[] = "mybuffer";
void *data = malloc(strlen(buf)+1);
strcpy((char*)data,buf);
Actually if data is really to be defined you can also do
char buf[] = "mybuffer";
void *data= (void*)strdup(buf);