2

I'm trying to write a program that does the following:

Enter a word: supercalifragilisticoespialidoso

The word's length is: 32.

Enter a smaller number than the length: 23

The word cut on letter 23 is: supercalifragilisticoes.

For that I'm doing:

#include <stdio.h>

#include<string.h>

#define DIM 99

int main() {

    char name[DIM], name2[DIM];
    int length, length2;

    printf("Enter a word: ");
    scanf("%s", name);
    length = strlen(name);
    printf("The word's length is: %d\n", length);
    printf("Enter a smaller number than the length: ");
    scanf("%d", &length2);
    name2 = name[length2];
    printf("The word cut on the letter %d is: %c", length2, name2);
    
    
    return 0;
}

But I get

main.c:16:11: error: assignment to expression with array type

The problem is in the line name2 = name[length2], that's the way I found to create the new smaller word, but it's not right.

Could someone please help?

Clifford
  • 88,407
  • 13
  • 85
  • 165
guiromero
  • 79
  • 5
  • Your way doesn't work. You try to assign the character set position length 2 to an entire array. – infinitezero Aug 14 '21 at 13:47
  • You can try to set the termination character of the string earlier or use a loop. – infinitezero Aug 14 '21 at 13:48
  • I'm not trying to be rude to you, but you should not have thought that `name2 = name[length2]` was the way to do this. The bracket characters `[]` are for indexing things in an array. So `name[length2]` gets you one character out of the string, it does not get you all characters `name[0..length2]`. – Steve Summit Aug 14 '21 at 13:58
  • Furthermore, you need to know that C gives you very few built-in operations on strings. In the abstract (but *not* in C), what you're looking for is something like `name2 = substr(name, 0, length2)`. Some programming languages have a `substr` function that works like this, but C does not. And the reason C does not is that it is basically impossible for a function (any function) in C to return a new string like that. – Steve Summit Aug 14 '21 at 14:00
  • When you wrote `name2 = name[length2]`, I think that was probably a guess, a shot in the dark. But guessing is a very poor way to learn programming! – Steve Summit Aug 14 '21 at 14:01
  • The error `error: assignment to expression with array type` is telling you that, in C, you can *never* assign one array to another like this, which means you can never assign one string to another like this (because strings are arrays in C). If `a` is an array, you can never say `a = anything`, no matter what the `anything` is. It just doesn't work. (The technical reason is that arrays are "second class citizens" in C.) – Steve Summit Aug 14 '21 at 14:05
  • he error message is reported for line 16, but line 16 is not the line you have indicated - the code shown is clearly not the code you complied. It is best to keep the code and the log consistent. – Clifford Aug 14 '21 at 14:32
  • Just an FYI — the spelling is normally [supercalifragilisticexpialidocious](https://www.dictionary.com/browse/supercalifragilisticexpialidocious). – Jonathan Leffler Aug 14 '21 at 16:35

4 Answers4

4

The mistake is in the line

name2 = name[length2];

You're trying to assign a character (the one of index length2 inside name) to an array (name2).

What you actually want to do is this:

strncpy(name2, name, length2);
name2[length2] = '\0';

This copies the first length2 bytes of name into name2 and adds a terminating null character for safety (strncpy doesn't do it if all of the bytes are written).

If you don't intend to use name again, you could as well remove name2 altogether and add a null character to name:

name[length2] = '\0';

You're also printing a string with a %c format specifier on the last printf call. You should use %s instead.

altermetax
  • 696
  • 7
  • 16
  • 1
    Make that `name2[length2] = '\0'` in the `strncpy()` case. `strncpy()` does not terminate the destination string if it doesn't find a terminator in the specified initial substring of the source. One might luck out and find `name2` to initially be zero-filled, but that is in no way guaranteed for local arrays, and relying on such a thing is a dangerous habit to form. – John Bollinger Aug 14 '21 at 13:54
  • 1
    And what of the issue trying to print the resultant string with the `%c` format specifier? – Adrian Mole Aug 14 '21 at 14:04
4

Other answers have suggested how you might make a copy of the initial substring of your input or how to modify the input string in place. Those are perfectly good approaches, and both have plenty of uses in real-world programs.

However, if all your program needs to do is print the wanted substring then there is no need to do any string manipulation at all. printf can do the job by itself. Given the variables as you declared them and this code ...

    scanf("%s", name);
    length = strlen(name);
    printf("The word's length is: %d\n", length);
    printf("Enter a smaller number than the length: ");
    scanf("%d", &length2);

... you can use a printf format to print name into a bounded-length field whose length is given by another printf argument:

    printf("%.*s\n", length2, name);

That also adds a newline after, which is usually what one wants, but you can leave that off if you prefer.

The .* in the formatting directive indicates that a "precision" is being specified for the field via a printf argument. Other variations can express a fixed precision directly in the format. The significance of a precision depends somewhat on the directive type, but for s directives it is the maximum number of characters to be printed from the corresponding string argument.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
2

There are two main errors in your code, and a couple of other minor points.

First, you can't directly assign arrays (be they character strings or any other array type) in C; for nul-terminated char arrays (strings), you can copy one to another using the strcpy function, or copy part of one to another using strncpy (which is what you want in your case).

Second, you can't print a string using the %c format specifier – you need %s for those.

And, a less serious issue (but one to avoid, if you want to be a good programmer) is that functions that work with string lengths (like strlen and strncpy) generally use size_t types, rather than int; and these require using the %zu format specifier, in place of %d.

Here's a version of your code that does what you want:

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

#define DIM 99

int main()
{
    char name[DIM], name2[DIM] = { 0 }; // Fill with zeros to start, so we will have a nul terminator
    size_t length, length2; // Strictly, use "size_t" for "strlen" and "strncpy"
    printf("Enter a word: ");
    scanf("%s", name);
    length = strlen(name);
    printf("The word's length is: %zu\n", length); // Use "%zu" for the "size_t" type
    printf("Enter a smaller number than the length: ");
    scanf("%zu", &length2); //Again, "%zu" for "size_t"
    strncpy(name2, name, length2); // Use this function to copy a substring!
    printf("The word cut on the letter %zu is: %s", length2, name2);// Note that you need "%s" to print a string
    return 0;
}

There are some other 'safety measures' that you can add to your code, to prevent buffer overruns and other faults. One would be to limit the initial string input to at most DIM - 1 characters; this would be trivial if you had a hard-coded value of 99 in place of DIM, because then you could use a call like the following:

scanf("%98s", name); // Read at most 98 characters (leaving space for the nul-terminator)

However, the macro DIM cannot be used inside the quoted format string. Instead, you can write its value (minus 1) into a separate string and use that as the format argument to scanf; so, we replace our initial scanf call, like so:

//  scanf("%s", name); // This has the potential to overrun the "name" array!
    char fmt[8];
    sprintf(fmt, "%%%ds", DIM - 1); // Write "%98s" into the "fmt" string ...
    scanf(fmt, name);               // ...and use that for the "scanf" format

(Note that some compilers will warn about not using a string literal for the format argument, and some programmers may not 'approve' of doing so; however, it is perfectly legal C and, IMHO, a valid use of the scanf function.)

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 2
    You need to explicitly add a terminator to `name2` after the `strncpy` call. `strncpy()` will not do that itself in this case, and `name2`'s contents are initially indeterminate. – John Bollinger Aug 14 '21 at 13:59
  • @JohnBollinger I was editing while you were commenting. – Adrian Mole Aug 14 '21 at 13:59
  • @Clifford From [cppreference](https://en.cppreference.com/w/c/string/byte/strncpy): *If count is reached before the entire array src was copied, the resulting character array is not null-terminated.* Thus, in most cases for the code here, the destination string will **not** be automatically null-terminated. (It seems daft, I know, but that's the way `strncpy` works!) – Adrian Mole Aug 14 '21 at 15:02
  • @AdrianMole : Yes - I misunderstood the question and thought it was to print the substring `&name[length2]` - (i..e the _end_, not the _beginning_) my error. The semantic intent of the original code was misleading. – Clifford Aug 14 '21 at 15:05
  • I think there's a case to be made for ensuring that the lengths given are smaller than `DIM`. There's also a case to be made for ensuring that `scanf()` returns `1` each time it is used. The string input could limit the data to `DIM - 1` (which `scanf()` makes regrettably difficult to do). – Jonathan Leffler Aug 14 '21 at 16:39
  • @Jonathan Good points (as would be expected from you). I have added an edit to address the potential buffer overflow on input; I shall think about further edits to address your other concerns. – Adrian Mole Aug 14 '21 at 16:56
  • If you use `char name[DIM + 1];` you can also use `#define STREXPAND(x) #x`, `#define STRINGIFY(x) STREXPAND(x)` and then `if (scanf("%" STRINGIFY(DIM) "s", name) == 1) { …OK… } else { …trouble… }` — but only as long as `DIM` is a simple numeric macro (`99` etc) and not any more complex expression. And it must be a macro, not an enumeration. – Jonathan Leffler Aug 14 '21 at 17:04
  • @JonathanLeffler Yes, that would also work (subject to the limitations you mention) but I find stringifying macros rather more complicated than adding a few lines of code to actually write what you want. It's very subjective, though. – Adrian Mole Aug 14 '21 at 17:06
  • If you look at [How to prevent `scanf()` causing a buffer overflow in C?](https://stackoverflow.com/a/1621973/15168), you'll see me espousing the technique you use, albeit with a somewhat longer format buffer. And that is the general solution — the constraints imposed with the stringification of a macro are quite stringent and therefore not as general a solution. – Jonathan Leffler Aug 14 '21 at 17:10
0

The assignment:

name2 = name[length2];

does not have the semantics you appear to have assumed. Rather name2 is an array while name[length2] is a a single character at the index length2 of the array name.

In any event arrays are not first-class data types in C and you cannot assign one array to another (as it appears you were perhaps intending) let alone assigning a char for an array.

Here you might explicitly strncpy() to copy the sub-string, but in this case that is perhaps unnecessary. You can simply remove the name2 array and:

printf( "The word cut on the letter %d is: ", length2 ) ;
for( int i = 0; i < length && i < length2; i++ )
{
    putchar( name[i] ) ;
}

If you truly need to store the sub-string rather then simply outputting it then:

int i = 0 ;
for( i = 0; i < length && i < length2; i++ )
{
    name2[i] = name[i] ;
}
name2[i] = '\0' ;

Both have the advantage of behaving deterministically if length2 were less than zero or greater than length which you fail to check.

Using strncpy():

int len = (length2 < 0 || length2 > length) ? 
          length : 
          length2 ;
strncpy( name2, name, len ) ;
name2[len] = '\0' ; 

Of course all those length checks are largely defeated by the lack of safety in acquiring name in the manner you have in the first instance. Consider using fgets() instead, or even getchar() in a loop.

Clifford
  • 88,407
  • 13
  • 85
  • 165