6

Ok, so I'm a person who usually writes Java/C++, and I've just started getting into writing C. I'm currently writing a lexical analyser, and I can't stand how strings work in C, since I can't perform string arithmetic. So here's my question:

char* buffer = "";
char* line = "hello, world";

int i;
for (i = 0; i < strlen(line); i++) {
    buffer += line[i];
}

How can I do that in C? Since the code above isn't valid C, how can I do something like that? Basically I'm looping though a string line, and I'm trying to append each character to the buffer string.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
metro-man
  • 1,763
  • 2
  • 15
  • 28
  • Well if I'm looping through the line, how do I pass a single character through strcpy which takes (char*, const char*)? – metro-man Jul 15 '14 at 03:55
  • 1
    @user3839220: What you are trying to do is copy the string to `buffer`. So just replace your entire `for` loop with an `strcpy()` call. – sampathsris Jul 15 '14 at 03:56
  • possible duplicate of [Create a modifiable string literal in C++](http://stackoverflow.com/questions/3055240/create-a-modifiable-string-literal-in-c) – Sadique Jul 15 '14 at 03:56
  • 1
    @Krumia Well, this is a smaller example of what I'm actually doing. I want to be able to append characters to a char*, not copy two strings. – metro-man Jul 15 '14 at 03:58
  • You won't be able to modify `buffer` if it's declared as a pointer to a string literal. – McLovin Jul 15 '14 at 03:59
  • @al-Acme: The duplicate is a [tag:c++] question. Whereas this is [tag:c] – sampathsris Jul 15 '14 at 04:00
  • I guess i should have looked up this one [Modifying String Literal](http://stackoverflow.com/questions/5464183/modifying-string-literal) – Sadique Jul 15 '14 at 04:03

7 Answers7

2

First off the buffer needs to have or exceed the length of the data being copied to it.

char a[length], b[] = "string";

Then the characters are copied to the buffer.

int i = 0;
while (i < length && b[i] != '\0') { a[i] = b[i]; i++; }
a[i] = '\0';

You can reverse the order if you need to, just start i at the smallest length value among the two strings, and decrement the value instead of increment. You can also use the heap, as others have suggested, ordinate towards an arbitrary or changing value of length. Furthermore, you can change up the snippet with pointers (and to give you a better idea of what is happening):

int i = 0;
char *j = a, *k = b;
while (j - a < length && *k) { *(j++) =  *(k++); }
*j = '\0';

Make sure to look up memcpy; and don't forget null terminators (oops).

motoku
  • 1,571
  • 1
  • 21
  • 49
2

string literals are immutable in C. Modifying one causes Undefined Behavior.

If you use a char array (your buffer) big enough to hold your characters, you can still modify its content :

#include <stdio.h>

int main(void) {


    char * line = "hello, world";
    char buffer[32]; // ok, this array is big enough for our operation

    int i;
    for (i = 0; i < strlen(line) + 1; i++) 
    {
        buffer[i] = line[i];
    }

    printf("buffer : %s", buffer);

    return 0;
}
quantdev
  • 23,517
  • 5
  • 55
  • 88
1
#include <string.h>

//...

char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );

strcpy( buffer, line );

Though in C string literals have types of non-const arrays it is better to declare pointers initialized by string literals with qualifier const:

const char *line = "hello, world";

String literals in C/C++ are immutable.

If you want to append characters then the code can look the following way (each character of line is appended to buffer in a loop)

#include <string.h>

//...

char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );

buffer[0] = '\0';
char *p = Buffer;

for ( size_t i = 0; i < strlen( line ); i++ )
{
    *p++ = line[i];
    *p = '\0';
}

The general approach is that you find the pointer to the terminating zero substitute it for the target character advance the pointer and appenf the new terminating zero. The source buffer shall be large enough to accomodate one more character.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

If you want to append a single character to a string allocated on the heap, here's one way to do it:

size_t length = strlen(buffer);
char *newbuffer = realloc(buffer, length + 2);
if (newbuffer) {  // realloc succeeded
  buffer = newbuffer;
  buffer[length] = newcharacter;
  buffer[length + 1] = '\0';
}
else {  // realloc failed
  // TODO handle error...
  free(buffer);  // for example
}

However, this is inefficient to do repeatedly in a loop, because you'll be repeatedly calling strlen() on (essentially) the same string, and reallocating the buffer to fit one more character each time.

If you want to be smarter about your reallocations, keep track of the buffer's current allocated capacity separately from the length of the string within it — if you know C++, think of the difference between a std::string object's "size" and its "capacity" — and when it's necessary to reallocate, multiply the buffer's size by a scaling factor (e.g. double it) instead of adding 1, so that the number of reallocations is O(log n) instead of O(n).

This is the sort of thing that a good string class would do in C++. In C, you'll probably want to move this buffer-management stuff into its own module.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
  • This is what I've tried, but it always seems to hang when I run the executable? – metro-man Jul 15 '14 at 04:10
  • I haven't seen your code so I can't tell you why it hangs. Use a debugger and/or ask another question with more details about that problem. – Wyzard Jul 15 '14 at 04:17
  • No idea how to put code in this section here, so I'll edit my post. – metro-man Jul 15 '14 at 04:19
  • No, don't repurpose this question to ask a different question after people have already written answers to the original one. Just post another question. – Wyzard Jul 15 '14 at 04:19
  • Can only post every 90 minutes, just figure it out on my own :p – metro-man Jul 15 '14 at 04:29
0

The simplest solution, lacking any context, is to do:

char buffer[ strlen(line) + 1 ];
strcpy(buffer, line);

You may be used to using pointers for everything in Java (since non-primitive types in Java are actually more like shared pointers than anything else). However you don't necessarily have to do this in C and it can be a pain if you do.

Maybe a good idea given your background would be to use a counted string object in C, where the string object owns its data. Write struct my_string { char *data; size_t length; } . Write functions for creating, destroying, duplicating, and any other operation you need such as appending a character, or checking the length. (Separate interface from implementation!) A useful addition to this would be to make it allocate 1 more byte than length, so that you can have a function which null-terminates and allows it to be passed to a function that expects a read-only C-style string.

The only real pitfall here is to remember to call a function when you are doing a copy operation, instead of allowing structure assignment to happen. (You can use structure assignment for a move operation of course!)

M.M
  • 138,810
  • 21
  • 208
  • 365
0

The asprintf function is very useful for building strings, and is available on GNU-based systems (Linux), or most *BSD based systems. You can do things like:

char *buffer;
if (asprintf(&buffer, "%s: adding some stuff %d - %s", str1, number, str2) < 0) {
    fprintf(stderr, "Oops -- out of memory\n");
    exit(1); }
printf("created the string \"%s\"\n", buffer);
free(buffer);  /* done with it */
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
0

Appending is best done with snprintf

Include the stdio.h header

#include <stdio.h>

then

char* buffer;
char line[] = "hello, world";

// Initialise the pointer to an empty string
snprintf(buffer, 1, "%s", "");

for (i = 0; i < strlen(line); ++i) {
    snprintf(buffer, sizeof line[i], "%s%s", buffer, line[i]);
}

As you have started the code you have there is different from the question you are asking. You could have split the line with strtok though. But I hope my answer clarifies it.

surge10
  • 622
  • 6
  • 18