Passing the value of a string array to another function - c

I'm currently in my 1st year of programming and have solved the problem to some code using string arrays but have no idea why it worked. We are still studying pointers but I wanted to try arrays for a bit.
I wanted to pass the value of a string from the main function, to pass to another function, to compare it with another string. If the two strings are equal, it prints "equal" otherwise, it prints "not equal". Although I've solved the problem , I'm confused by how this works:
char name1[100];
scanf("%s",name1);
getname(name1[0]);
return 0;
}
void getname(char name1[0]){
printf("Name1 is %s",name1);
return;
}
If you run it this way, it will not run the function and will return a garbage value. However, if I change
getname(name1[0]);
into
getname(&name1[0]);
it works perfectly fine, why is this?. Secondly, in the "getname" function. since I had to add the ampersand/address to the function call, I was taught that I needed to add an asterisk next to the variable to avoid getting the actual address but to get the actual value. Why is it in
printf("Name1 is %s",name1);
it is able to print the string perfectly fine without needing it to be:
printf("Name1 is %s",*name1);

void getname(char name1[0]) formally says you're going to pass a character array of zero size (side note: zero size arrays are not allowed in standard C but compilers sometimes allow them).
What it really says is you're going to pass a character pointer, a char *. The size is ignored. It's equivalent to the less confusing: void getname(char *name1).
In addition, since you're not going to alter the string inside function you can declare it const char * and be warned if you try to alter it.
#include <stdio.h>
void getname(const char *name1){
printf("Name1 is %s",name1);
return;
}
int main() {
char name[100] = "foo";
getname(name);
}
getname(name1[0]); gives you garbage because instead of passing in the pointer to the start of the string, you're passing in the first character of the string. If the string is "Yarrow Hock" you're trying to pass in Y which is really the number 89. C will then try to read what's at memory address 89 which is probably garbage.
You can see this if you print the memory address of name1 using %p.
void getname(const char *name1){
printf("Name1 points to %p\n",name1);
printf("Name1 is %s\n",name1);
return;
}
You should get a warning like:
incompatible integer to pointer conversion passing 'char' to parameter of
type 'const char *'; take the address with & [-Wint-conversion]
Which leads us to a very important thing: compiler warnings. They are extremely important and by default they are off. Turning them on will save you hours of head scratching. There are a bewildering array of possible warnings and they're slightly different from compiler to compiler. Here's what I use with clang.
-Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c11 -pedantic
You can read what each of these mean in Diagnostic flags in Clang. And yes, "-Wall" does not mean "all warnings".
A good code editor, I recommend Atom, will also give you warnings as you edit.
getname(&name1[0]); works because it is equivalent to getname(name1 + 0) which is equivalent to getname(name1). name1[0] is the first character of the string. & gets its address, which is the start of the string just like name1 itself. printf then prints characters until it hits a null byte.
You can see this by trying getname(&name1[1]), or more directly with pointer arithmetic as getname(name1 + 1). This will pass in the address of the second character in name1. If name1 = "Yarrow Hock" it will print arrow Hock. This is a valid technique for skipping prefixes without altering the string.

Related

How to check if command line arguments are lower case/ alphabetic and the sort?

I am really new to C programming (have previously done python and this is quite a difficult transition). I have been given a task to check the command line arguments and those which are not lower case alphabetic or numeric should be printed out with a given statement.
I have tried using a for loop and tried checking islower for argv[i] but that just crashes my program. The compiler gives this warning:
passing argument 1 of 'islower' makes integer from pointer without a cast
Can someone please give a general idea without actually typing out the code? Please do no type the code as I really want to do this myself (and also might get into trouble for plagiarism).
islower() checks the case of a single character, whereas argv is a char ** variable, meaning that argv[i] is a string (a char *), not a character. Thus, to check whether a particular argument is lower-case, you need to iterate over the characters in it to check each one.
Which is also the meaning of the warning message you mention. Since the argument to islower() is an int (a single character), and you pass it a pointer (that is, argv[i], which is a char *), the compiler implicitly casts that pointer to the int that islower() requires, which is seldom intended behavior.

Why are strings in C declared with 'const'?

For example, why not:
char *s= "example";
instead of:
const char *s= "example";
I understand that const makes it unchangeable, but why do I receive an error when compiling the first?
Additionally, how does the concept apply to
int * x;
vs
const int *x;
I see the second used a lot more, is it good practice to use "cons int *"?
There's no requirement to use const, but it's a good idea.
In C, a string literal is an expression of type char[N], where N is the length of the string plus 1 (for the terminating '\0' null character). But attempting to modify the array that corresponds to the string literal has undefined behavior. Many compilers arrange for that array to be stored in read-only memory (not physical ROM, but memory that's marked read-only by the operating system). (An array expression is, in most contexts converted to a pointer expression referring to the initial element of the array object.)
It would have made more sense to make string literals const, but the const keyword did not exist in old versions of C, and it would have broken existing code. (C++ did make string literals const).
This:
char *s= "example"; /* not recommended */
is actually perfectly valid in C, but it's potentially dangerous. If, after this declaration, you do:
s[0] = 'E';
then you're attempting to modify the string literal, and the behavior is undefined.
This:
const char *s= "example"; /* recommended */
is also valid; the char* value that results from evaluating the string literal is safely and quietly converted to const char*. And it's generally better than the first version because it lets the compiler warn you if you attempt to modify the string literal (it's better to catch errors at compile time than at run time).
If you get an error on your first example, then it's likely that you're inadvertently compiling your code as C++ rather than as C -- or that you're using gcc's -Wwrite-strings option or something similar. (-Wwrite-strings makes string literals const; it can improve safety, but it can also cause gcc to reject, or at least warn about, valid C code.)
With Visual Studio 2015 at warning level 4, this compiles and runs whether compiled as C or C++:
#include <stdio.h>
char *s1= "example\n";
const char *s2= "example\n";
int main(int argc, char **argv)
{
printf(s1); // prints "example"
s1[2] = 'x';
printf(s1); // prints "exxmple"
printf(s2);
return 0;
}
If I add this line, it will fail to compile as C or C++ with every compiler I know of:
s2[2] = 'x'; // produces compile error
This is the error the const keyword is designed to avoid. It simply tells the compiler not to allow assignments to the object pointed to.
It doesn't matter if your pointer points to char or int or anything else. The const keyword has the same effect on all pointers, and that's to make it impossible (well, very hard) to assign to the thing declared const.
A string literal used as a value compiles to an array of char that should not be modified. Attempting to modify it invokes undefined behavior. For historical reasons of backward compatibility, its type is char [] although is really should be const char []. You can enable extra compiler warnings to change this and instruct the compiler to consider such strings to be const.

What should happen, when we try to modify a string constant?

#include<stdio.h>
#include<string.h>
int main()
{
int i, n;
char *x="Alice"; // ....... 1
n = strlen(x); // ....... 2
*x = x[n]; // ....... 3
for(i=0; i<=n; i++)
{
printf("%s ", x);
x++;
}
printf("\n");
return 0;
}
String constant cannot be modified. In the above code *x means 'A'. In line 3 we are trying to modify a string constant. Is it correct to write that statement? When I run this code on Linux, I got segmentation fault. But on www.indiabix.com, they have given answer:
If you compile and execute this program in windows platform with Turbo C, it will give lice ice ce e It may give different output in other platforms (depends upon compiler and machine). The online C compiler given in this site will give Alice lice ice ce e as output (it runs on Linux platform).
Your analysis is correct. The line
*x = x[n];
is trying to modify a string literal, so it's undefined behavior.
BTW, I checked the website that you linked. Just browsing it for two minutes, I've already found multiple incorrect code samples (to name a few, using gets, using char(not int) to assign return value of getchar, etc), so my suggestion is don't use it.
Your analysis is correct, but doesn't contradict what you quoted.
The code is broken. The answer already acknowledges that it may behave differently on different implementations, and has given two different outputs by two different implementations. You happen to have found an implementation that behaves in a third way. That's perfectly fine.
Modification of a string literal is Undefined Behaviour. So the behaviour you observe, and the two described, are consistent with the requirements of the C standard (as is emailing your boss and your spouse, or making demons fly out of your nose). Those three are all actually quite reasonable actions (modify the 'constant', ignore the write, or signal an error).
With GCC, you can ask to be warned when you assign the address of a string literal to a pointer to (writable) char:
cc -g -Wall -Wextra -Wwrite-strings -c -o 27211884.o 27211884.c
27211884.c: In function ‘main’:
27211884.c:7:13: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
char *x="Alice"; // ....... 1
^
This warning is on by default when compiling C++, but not for C, because char* is often used for string literals in old codebases. I recommend using it when writing new code.
There are two correct ways to write the code of the example, depending on whether you want your string to actually be constant or not:
const char *x = "Alice";
char x[] = "Alice";
In this code, the memory for "Alice" will be in the read-only data section of the executable file and x is a pointer pointing to that read-only location. When we try to modify the read-only data section, it should not allow this. But char *x="Alice"; is telling the compiler that x is declared as a pointer to a character, i.e. x is pointing to a character which can be modified (i.e. is not read-only). So the compiler will think that it can be modified. Thus the line *x = x[n]; will behave differently on different compilers. So it will be undefined behavior.
The correct way of declaring a pointer to a assign string literal is as below:
const char *x ="Alice";
Only then can the behavior of the compiler be predicted.

Using a volatile string as the format string with printf()

Suppose I have the following function signature:
int printf(const char * restrict format, ... );
Now, I have a string defined as follows:
volatile char command_str[256];
Now, when I want to pass this string to my printf function, I will get the following warning:
Warning 32 [N] 2943 : passing 'volatile char [256]' to parameter of type 'const char *' discards qualifiers C:\P\parameter\parameter.c
I do not want to change the printf signature, the easiest solution to make the warning go away would be
printf((const char*)command_str, .......);
I have a feeling that this is not the best solution. What would be the correct thing to do? I cannot make command_str non-volatile since it is accessed within an interrupt.
the const in printf()'s signature declares a promise printf() makes -- it won't mess with the data pointed to by format (therefore, both char* and const char* variables may be passed in for format).
Now, your array is volatile (and I expect you know the implication of that). The compiler warns you, that this volatility is discarded in printf()'s scope -- you won't get volatile semantics for accesses to format within printf().
As a suggestion what to do, I'd say evaluate whether you really want changes to the data be apparent midst- printf(). I can't see a reason for wanting that, so making a local copy sounds reasonable.
The function are are passing to (printf()) expects the string to be mutable (const * means that printf() will not modify the content, not to be confused!), and the string you are trying to pass will get modified (well, to be precise the pointer to the string) by an interrupt.
How can you be you be sure that an interrupt will not modify the contents of the string between you calling printf() and printf() actually printing...? What prevents the interrupt from happening while printf() is working?
You need to mask interrupts while calling printf() (using ASM {"CLI"} or something more applicable to your platform), or just copy the string you pass to printf():
// point a
char s[256];
strncpy(s, command_str, 256);
// point b
printf("%s", s);
// point c
This will fix the problem for printf(), but now you have a new race condition point a and b. I think you need to refactor your code. You have bigger issues.
One solution might be:
char s[256];
mask_interrupts();
strncpy(s, command_str, 256);
unmask_interrupts();
printf("%s", s");

C programming language, array, pointer

int main()
{
int j=97;
char arr[4]="Abc";
printf(arr,j);
getch();
return 0;
}
this code gives me a stack overflow error why?
But if instead of printf(arr,j) we use printf(arr) then it prints Abc.
please tell me how printf works , means 1st argument is const char* type so how arr is
treated by compiler.
sorry! above code is right it doesn't give any error,I write this by mistake. but below code give stack overflow error.
#include <stdio.h>
int main()
{
int i, a[i];
getch();
return 0;
}
since variable i take any garbage value so that will be the size of the array
so why this code give this error when i use DEV C++ and if I use TURBO C++ 3.0 then
error:constant expression required displayed. if size of array can't be variable then when
we take size of array through user input.no error is displayed. but why in this case.
please tell me how printf works
First of all, pass only non-user supplied or validated strings to the first argument of printf()!
printf() accepts a variable number of arguments after the required const char* argument (because printf() is what's called a variadic function). The first const char* argument is where you pass a format string so that printf() knows how to display the rest of your arguments.
If the arr character array contains user-inputted values, then it may cause a segfault if the string happens to contain those formatting placeholders, so the format string should always be a hard-coded constant (or validated) string. Your code sample is simple enough to see that it's really a constant, but it's still good practice to get used to printf("%s", arr) to display strings instead of passing them directly to the first argument (unless you absolutely have to of course).
That being said, you use the formatting placeholders (those that start with %) to format the output. If you want to display:
Abc 97
Then your call to printf() should be:
printf("%s %d", arr, j);
The %s tells printf() that the second argument should be interpreted as a pointer to a null-terminated string. The %d tells printf() that the third argument should be interpreted as a signed decimal.
this code gives me a stack overflow error why?
See AndreyT's answer.
I see that now the OP changed the description of the behavior to something totally different, so my explanation no longer applies to his code. Nevertheless, the points I made about variadic functions still stand.
This code results in stack invalidation (or something similar) because you failed to declare function printf. printf is a so called variadic function, it takes variable number of arguments. In C language it has [almost] always been mandatory to declare variadic functions before calling them. The practical reason for this requirement is that variadic functions might (and often will) require some special approach for argument passing. It is often called a calling convention. If you forget to declare a variadic function before calling it, a pre-C99 compiler will assume that it is an ordinary non-variadic function and call it as an ordinary function. I.e. it will use a wrong calling convention, which in turn will lead to stack invalidation. This all depends on the implementation: some might even appear to "work" fine, some will crash. But in any case you absolutely have to declare variadic functions before calling them.
In this case you should include <stdio.h> before calling printf. Header file <stdio.h> is a standard header that contains the declaration of printf. You forgot to do it; hence the error (most likely). There's no way to be 100% sure, since it depends on the implementation.
Otherwise, your code is valid. The code is weird, since you are passing j to printf without supplying a format specifier for it, but it is not an error - printf simply ignores extra variadic arguments. Your code should print Abc in any case. Add #include <stdio.h> at the beginning of your code, and it should work fine, assuming it does what you wanted it to do.
Again, this code
#include <stdio.h>
int main()
{
int j=97;
char arr[4]="Abc";
printf(arr,j);
return 0;
}
is a strange, but perfectly valid C program with a perfectly defined output (adding \n at the end of the output would be a good idea though).
In your line int i, a[i]; in the corrected sample of broken code, a is a variable-length array of i elements, but i is uninitialized. Thus your program has undefined behavior.
You see strings in C language are treated as char* and printf function can print a string directly. For printing strings using this function you should use such code:
printf("%s", arr);
%s tells the function that the first variable will be char*.
If you want to print both arr and j you should define the format first:
printf("%s%d", arr, j);
%d tells the function that the second variable will be int
I suspect the printf() issue is a red herring, since with a null-terminated "Abc" will ignore other arguments.
Have you debugged your program? If not can you be sure the fault isn't in getch()?
I cannot duplicate your issue but then I commented out the getch() for simplicity.
BTW, why did you not use fgetc() or getchar()? Are you intending to use curses in a larger program?
===== Added after your edit =====
Okay, not a red herring, just a mistake by the OP.
C++ does allow allocating an array with the size specified by a variable; you've essentially done this with random (garbage) size and overflowed the stack, as you deduced. When you compile with C++ you are typically no longer compiling C and the rules change (depending on the particular compiler).
That said, I don't understand your question - you need to be a lot more clear with "when we take size of array through user input" ...

Resources