78

Right now I'm trying this:

#include <stdio.h>

int main(int argc, char *argv[]) {

    if (argc != 3) {

        printf("Usage: %s %s sourcecode input", argv[0], argv[1]);
    }
    else {
        char source[] = "This is an example.";
        int i;

        for (i = 0; i < sizeof(source); i++) {

            printf("%c", source[i]);
        }
    }

    getchar();

    return 0;
}

This does also NOT work:

char *source = "This is an example.";
int i;

for (i = 0; i < strlen(source); i++){

    printf("%c", source[i]);
}

I get the error

Unhandled exception at 0x5bf714cf (msvcr100d.dll) in Test.exe: 0xC0000005: Access violation while reading at position 0x00000054.

(loosely translated from german)

So what's wrong with my code?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vincent
  • 3,965
  • 2
  • 22
  • 30
  • 7
    please don't edit the code that you asked about. That changes your question quite a bit so that many of the answers are irrelevant. Instead, just post all the things you have tried and mention which of them were in response to answers. – Michael Myers Jul 09 '10 at 15:13
  • The new test against argc you added is wrong. –  Jul 09 '10 at 15:17

14 Answers14

87

You want:

for (i = 0; i < strlen(source); i++) {

sizeof gives you the size of the pointer, not the string. However, it would have worked if you had declared the pointer as an array:

char source[] = "This is an example.";

but if you pass the array to function, that too will decay to a pointer. For strings it's best to always use strlen. And note what others have said about changing printf to use %c. And also, taking mmyers comments on efficiency into account, it would be better to move the call to strlen out of the loop:

int len = strlen(source);
for (i = 0; i < len; i++) {

or rewrite the loop:

for (i = 0; source[i] != 0; i++) {
pevik
  • 4,523
  • 3
  • 33
  • 44
  • 2
    Uh, I kind of doubt he wants to use `strlen` as a bound. (Although granted, in a 10-line program it doesn't make much difference.) – Michael Myers Jul 09 '10 at 15:01
  • 23
    Well, last I checked, strlen had to iterate through all characters to figure out the length. And since the char* could change in the loop, the strlen can't be hoisted out of the bounds check by the compiler; that makes the loop O(n^2) instead of O(n). Please correct me if I'm wrong. – Michael Myers Jul 09 '10 at 15:06
  • 1
    In this example, a good compiler could determine that the string does not change and omit repeated calls to strlen. But it's probably better practice to store the length in a variable prior to the loop. – R.. GitHub STOP HELPING ICE Jul 09 '10 at 15:09
  • 3
    @Neil Butterworth: The difference between for (i=0, max=strlen(str); i – Vatine Jul 09 '10 at 15:09
  • @Valine But does it matter? anywy, changed answer to account for this. –  Jul 09 '10 at 15:10
  • 1
    @anon Sure, it doesn't matter when writing a program that prints one string of 21 characters, but the difference between O(n^2) and O(n) will make itself very evident when it does matter. – Elfen Dew Apr 29 '18 at 18:05
62

One common idiom is:

char* c = source;
while (*c) putchar(*c++);

A few notes:

  • In C, strings are null-terminated. You iterate while the read character is not the null character.
  • *c++ increments c and returns the dereferenced old value of c.
  • printf("%s") prints a null-terminated string, not a char. This is the cause of your access violation.
Dave Yarwood
  • 2,866
  • 1
  • 17
  • 29
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • 5
    Can I use `while (*c++) putc(*c);`? – Incerteza Sep 06 '14 at 15:25
  • 1
    -1 "In C, strings are zero terminated." This makes no sense. Maybe what you're trying to say is c-strings should be null terminated. – Celeritas Sep 22 '14 at 04:06
  • 7
    @Celeritas: I find the downvote a bit harsh -- C has language support for zero-terminated strings, and the OP is using those. Granted, you can use counted strings (eg. `BSTR`s in the windows world), but they are less common, and the question is not ambiguous as to which string type one is using. – Alexandre C. Sep 22 '14 at 18:20
  • How about this? Initially when I google searched "zero terminated" virtually nothing came back regaurding strings. On a deeper look, there are some [vaguage](https://news.ycombinator.com/item?id=1401327) [mentions](http://stackoverflow.com/questions/2911089/null-terminating-a-string) of "zero terminated". If you edit the answer to explain what a zero terminated string is and how they differ from null terminated, I will change the vote. – Celeritas Sep 22 '14 at 19:36
  • 8
    Oh, you're just talking about how they are called ? Zero-terminated is rather a name from the Win32 API world (where string variables are stupidly prefixed by `sz`), and null-terminated is a more "traditional" (and common) name. Anyway, they both mean the same. – Alexandre C. Sep 23 '14 at 17:54
  • For more complex cases, with larger loop bodies, `for (char *c = source; *c; c++) { putc(*c); }` may be easier to understand. – squirl Jan 04 '17 at 15:01
  • @Samadi: matter of taste (and team conventions :) ) – Alexandre C. Jan 04 '17 at 15:03
  • @AlexandreC. true. – squirl Jan 04 '17 at 19:13
  • @AlexandreC. I was so confused by `while (*str) ++str;` in some code I came across, your answer was amazingly helpful. Thanks! – Sienna Mar 10 '17 at 20:37
  • 2
    Is this [`putc`](https://en.wikibooks.org/wiki/C_Programming/stdio.h/putc) from ``? Because it appears that that function expects a second argument, a stream. [`putchar`](https://en.wikibooks.org/wiki/C_Programming/stdio.h/putchar) can be used with just a single argument like this example is doing. – Dave Yarwood Sep 02 '17 at 14:30
  • @DaveYarwood: Yes, you are right. I will leave it to the reader anyway since the post is about iterating :) – Alexandre C. Sep 03 '17 at 08:19
  • @アレックス No, it'll skip the first character and also call `putchar(0)` at the end. [Try it yourself](https://godbolt.org/z/Mffnda). You need to increase it after its last mention/use. So this is ok: `int i=0; while (*c++) i++;` since we don't use `*c` after converting it to a boolean. – johv Aug 26 '20 at 12:54
5

Rather than use strlen as suggested above, you can just check for the NULL character:

#include <stdio.h>

int main(int argc, char *argv[])
{
    const char *const pszSource = "This is an example.";
    const char *pszChar = pszSource;

    while (pszChar != NULL && *pszChar != '\0')
    {
        printf("%s", *pszChar);
        ++pszChar;
    }

    getchar();

    return 0;
}
Mark Ingram
  • 71,849
  • 51
  • 176
  • 230
  • 2
    It'll work, but I'd recommend checking `pszChar != '\0'` to be explicit anyways. You aren't comparing against `NULL` (typically for pointers), but against the null character, which terminates C strings. (Alternatively, both of those are equal to zero, so `pszChar && *pszChar` is also a fine condition, if you're in to that sort of thing). – Matt Enright Apr 03 '13 at 13:11
5

An optimized approach:

for (char character = *string; character != '\0'; character = *++string)
{
    putchar(character); // Do something with character.
}

Most C strings are null-terminated, meaning that as soon as the character becomes a '\0' the loop should stop. The *++string is moving the pointer one byte, then dereferencing it, and the loop repeats.

The reason why this is more efficient than strlen() is because strlen already loops through the string to find the length, so you would effectively be looping twice (one more time than needed) with strlen().

2

sizeof(source) returns the number of bytes required by the pointer char*. You should replace it with strlen(source) which will be the length of the string you're trying to display.

Also, you should probably replace printf("%s",source[i]) with printf("%c",source[i]) since you're displaying a character.

Jacob
  • 34,255
  • 14
  • 110
  • 165
2

This should work

 #include <stdio.h>
 #include <string.h>

 int main(int argc, char *argv[]){

    char *source = "This is an example.";
    int length = (int)strlen(source); //sizeof(source)=sizeof(char *) = 4 on a 32 bit implementation
    for (int i = 0; i < length; i++) 
    {

       printf("%c", source[i]);

    }


 }
Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
2
  1. sizeof() includes the terminating null character. You should use strlen() (but put the call outside the loop and save it in a variable), but that's probably not what's causing the exception.
  2. you should use "%c", not "%s" in printf - you are printing a character, not a string.
KenE
  • 1,805
  • 10
  • 6
2

The last index of a C-String is always the integer value 0, hence the phrase "null terminated string". Since integer 0 is the same as the Boolean value false in C, you can use that to make a simple while clause for your for loop. When it hits the last index, it will find a zero and equate that to false, ending the for loop.

for(int i = 0; string[i]; i++) { printf("Char at position %d is %c\n", i, string[i]); }
Edwin Jata
  • 21
  • 4
1

Just change sizeof with strlen.

Like this:

char *source = "This is an example.";
int i;

for (i = 0; i < strlen(source); i++){

    printf("%c", source[i]);

}
Pablo Santa Cruz
  • 176,835
  • 32
  • 241
  • 292
1
  • sizeof(source) is returning to you the size of a char*, not the length of the string. You should be using strlen(source), and you should move that out of the loop, or else you'll be recalculating the size of the string every loop.
  • By printing with the %s format modifier, printf is looking for a char*, but you're actually passing a char. You should use the %c modifier.
JSBձոգչ
  • 40,684
  • 18
  • 101
  • 169
1

This is 11 years old but relevant to someone who is learning C. I don't understand why we have all this discussion and disagreement about something so fundamental. A string literal in C, I.E. "Text between quotes" has an implicit null terminator after the last character. Don't let the name confuse you. The null terminator is equal to numeric 0. Its purpose is exactly what OP needs it for:

char source[] = "This is an example.";

for (int i = 0; source[i]; i++)
  printf("%c", source[i]);

A char in C is an 8-bit integer with the numeric ASCII value of the corresponding character. That means source[i] is a positive integer until char[19], which is the null terminator after the final '.' The null character is ASCII 0. This is where the loop terminates. The loop iterates through every character with no regard for the length of the array.

Mark Lewin
  • 11
  • 1
0

Replace sizeof with strlen and it should work.

Keith Randall
  • 22,985
  • 2
  • 35
  • 54
0

You need a pointer to the first char to have an ANSI string.

printf("%s", source + i);

will do the job

Plus, of course you should have meant strlen(source), not sizeof(source).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ULysses
  • 978
  • 4
  • 9
0

sizeof(source) returns sizeof a pointer as source is declared as char *. Correct way to use it is strlen(source).

Next:

printf("%s",source[i]); 

expects string. i.e %s expects string but you are iterating in a loop to print each character. Hence use %c.

However your way of accessing(iterating) a string using the index i is correct and hence there are no other issues in it.

Praveen S
  • 10,355
  • 2
  • 43
  • 69