2

I was learning about loops when I stumbled upon this thing I don't understand.

In this code, if I enter in the for loop, i = 0, it works as intended, if I write "dog", I get 3 a's, but if I write i = -1, I don't get input at all, shouldn't give me an extra a?

Can't I start the i value wherever I want?

#include <string.h>

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

        scanf("%s", string);

        void changestring (char *newstring) {
                for (int i=0; i < strlen(newstring); i++) {
                        printf("a");
                }
        }
        changestring(string);
}
Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Iván
  • 15
  • 4
  • 3
    `strlen()` returns an *unsigned* value. Comparing an int with an unsigned value forces a conversion of the int. `(unsigned)-1 == 0xffffffff` and `0xffffffff > strlen(whatever)` – pmg Dec 08 '19 at 11:58
  • 1
    and don't use strlen inside a loop, it'll be called again and again which is very inefficient. Store the length in a variable instead – phuclv Dec 08 '19 at 12:27
  • Try `i < strlen(newstring)` --> `i < 0 || (unsigned)i < strlen(newstring)` – chux - Reinstate Monica Dec 08 '19 at 13:01

2 Answers2

3

First of all, let's rewrite your code a little bit, for the sake of removing some confusion (because to the uninitiated there are few things going on here, that might seem similar, but are not).

#include <string.h>

void print_as_according_to_strlen(char *n) {
    for (int i=0; i < strlen(newstring); i++) {
        printf("a");
    }
}

There, this is the relevant part. If you look at the definition of strlen you'll see, that it returns a value of type size_t

size_t strlen(const char *s);

Return the length of the string s.

size_t is an unsigned type. C and C++ have certain rules by which integer values are implicited converted to a "common" type when used together in an operation. These are the so called "integer promotion rules" – those are important to know, and you absolutely must learn them.

One of those rules is, that if you make a signed integer "interact" with an unsigned integer, the promoted type will be unsigned as well. And of course if you attempt to promote a negative value to an unsigned type, the result will be something different.

Now with C things become tricky here, because there are some (actually the majority of cases) where attempting to do that invokes undefined behavior, i.e. anything may happen. There are however a few exceptions, where it's actually well behaved (for example if you have two unsigned variables unsigned a = 3 and unsigned b = 5 the result of the operation a-b is actually a well defined value, because unsigned overflow is well defined, and the result is UINT_MAX - (b-a)).

The bottom line for you is: You must rewrite your code so that the comparison promotes to either a signed comparison, or instead of starting from -1, go to strlen(…)+1. Instead of using an int I suggest you use a ptrdiff_t instead, because the size of an int may, or may not, be sufficient to hold the result of strlen. And by happenstance (not of C, but how modern OSs manage memory) strlen will never return a value that could overflow ptrdiff_t

ptrdiff_t len = strlen(newstring);
for(ptrdiff_t i=-1; i < len ; i++){
datenwolf
  • 159,371
  • 13
  • 185
  • 298
0

For starters nested function definitions is not a C standard feature. You should defined the function outside main.

As for your question then in this expression

i < strlen(newstring)

the compiler determinates the common type of the operands of the operator < using the usual arithmetic conversions of the operands.

The returned type of the function strlen is size_t that is an unsigned type that has the conversion rank higher or equal to the rank of the type int. SO the expression i is converted to the unsigned type size_t preserving its bits. As a result you get a very big unsigned value that is gretaer than the value returned by the function strlen and the caondition evaluates to false.

Consider the following demonstrative program

#include <stdio.h>

int main(void) 
{
    int i = -1;

    printf( "i = %i, ( size_t )i = %zu\n", i, ( size_t  ) i );

    return 0;
}

Its output might look like

i = -1, ( size_t )i = 18446744073709551615
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335