How to correctly prototype C functions - c

I'm learning the concept of prototyping in C, however I'm struggling with the correct syntax. I'm writing a function to strip all non-alphbetic characters from a c-string
#include <stdio.h>
#include <string.h>
char[30] clean(char[30] );
int main()
{
char word[30] = "hello";
char cleanWord[30];
cleanWord = clean(word);
return 0;
}
char[30] clean(char word[30])
{
char cleanWord[30];
int i;
for(i=0;i<strlen(word);i++)
if ( isalpha(word[i]) )
cleanWord[i]=word[i];
cleanWord[i]='\0';
return cleanWord;
}
How do I correctly prototype the function? What are the other syntax errors that are preventing my program from compiling?

Your problem is not with function prototyping (aka forward declaration). You just can't return an array from a function in C. Nor can you assign to an array variable. You need to make a couple of changes to get things working. One option:
change char cleanWord[30] in main to be char * cleanWord.
change the signature of clean to char *clean(char word[30])
use malloc to allocate a destnation buffer inside clean
return a pointer to that new buffer
free the buffer in main
And another:
change the signature of clean to void clean(char word[30], char cleanWord[30])
operate on the passed-in pointer rather than a local array in clean
change the call in main to be clean(word, cleanWord).

As Carl Norum said, you can't return an array. Instead, what you tend to do is supply the output:
void clean( const char word[30], char cleanWord[30] )
{
}
And you should remove the locally-scoped array from that function.
You will find that the function does not work correctly, because you only have one iterator i. That means if a character is not an alpha, you will skip over a position in the output array. You will need a second iterator that is incremented only when you add a character to cleanWord.

A couple of notes (was a bit late with writing up an answer, seems I've been beaten to them by the others )
C cannot return local (stack) objects, if you want to return an array from a function you have to malloc it
Even if you declare an array argument as (char arr[30]), (char* arr) is just as valid as arrays decay to pointers when passed as arguments to functions. Also, you won't be able to get the size correctly of such arrays by using sizeof. Even though it's 30, on my machine it returns 4 for word in clean, which is the size of the pointer for it.
You are missing an include, isalpha is part of ctype.h
I've updated your code, hopefully I've guessed your intentions correctly:
#include <stdlib.h> /* for malloc and free */
#include <string.h> /* for strlen */
#include <ctype.h> /* for isalpha */
#include <stdio.h> /* for printf */
/* Function declaration */
char* clean(char word[30]);
/* your 'main()' would now look like this: */
int main()
{
char word[30] = "hel1lo1";
char* cleanWord;
cleanWord = clean(word);
printf("%s\n", cleanWord);
free(cleanWord);
return 0;
}
/* Function definition */
char* clean(char word[30])
{
char* cleanWord = malloc(30); /* allocating dynamically an array of 30 chars,
* no need to use sizeof here as char is
* guaranteed to be 1 by the standard
*/
unsigned int i, j = 0; /* let's fix the problem with non-alpha chars already pointed out */
for (i = 0; i < (strlen(word)); i++)
if (isalpha(word[i]))
cleanWord[j++] = word[i];
cleanWord[j] = '\0';
return cleanWord;
/* return a pointer to the malloc`ed array, don't forget to free it after you're done with it */
}

Related

Returning array of chars in C function

I have tried so many ways of doing this and I cannot get it to work. My setup says to use char[] but everything I've researched has no information on using that. Here is my code below
#include <stdio.h>
#include <string.h>
char[] makeString(char character,int count)
{
char finalString[count];
strcpy(finalString,character);
for(int i=1;i<=count;i++)
{
strcat(finalString,character);
}
return finalString;
}
int main() {
printf("%s\n",makeString("*",5)); }
I'm trying to create a function that returns a string of the given character count amount of times. Any help is greatly appreciated.
My apologies if this is a very simple error, I mostly code in python so C is very new to me.
There are a couple of issues, mainly on char finalString[count];
finalString is a variable created within the function, it is called a local variable. It would be destroyed after the function returns.
count as the value of the count variable is dynamically changed. Its value could not be determined during the compile stage, thus the compiler could not allocate memory space for this array. The compiling would fail.
To fix this issue.
either create this variable outside and pass it into the function. Or create the variable on the HEAP space. As the Heap space are shared across the entire program and it would not be affected by function ending.
either using a constant number or dynamically allocating a chunk of memory with malloc, calloc and etc.
Here is one demo with the full code:
// main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* makeString(char character,int count) {
char* finalString = malloc(count+1); //dynamic allocate a chunk of space. +1 for mimic string end with an extra termination sign 0.
for(int i=0; i<count; i++) {
finalString[i] = character;
}
finalString[count] = 0; // string end signal 0
return finalString; // the memory finalString pointed to is in the HEAP space.
}
int main() {
char * p = makeString('*',5);
printf("%s\n",p);
free(p); // free up the HEAP space
return 0;
}
To compile and run the code.
gcc -Wall main.c
./a.out
The output
*****
Arrays are not a first-class type in C -- there are a lot of things you can do with other types that you can't do with arrays. In particular you cannot pass an array as a parameter to a function or return one as the return value.
Because of this restriction, if you ever declare a function with an array type for a parameter or return type, the compiler will (silently) change it into a pointer, and you'll actually be passing or returning a pointer. That is what is happening here -- the return type gets changed to char *, and you return a pointer to your local array that is going out of scope, so the pointer you end up with is dangling.
Your code doesn't compile. If you want to return an array you do char * not char []. Local variables like finalString are out of scope after the function returns.
Here are 3 ways of doing it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *initString(char *a, char c, int n) {
memset(a, c, n);
a[n] = '\0';
return a;
}
char *makeString(char c, int n) {
char *a = malloc(n + 1);
memset(a, c, n);
a[n] = '\0';
return a;
}
int main(void) {
printf("%s\n", memset((char [6]) { 0 }, '*', 5));
char s[6];
initString(s, '*', (sizeof s / sizeof *s) - 1);
printf("%s\n", s);
char *s2 = makeString('*', 5);
printf("%s\n", s2);
free(s2);
}

Understanding double void pointers in C

I'm trying to do very basic examples to understand how void pointers work. Here's an example I've written for having a void* string and casting it to its "working" type and printing some aspects of it:
int main(int argc, char *argv[]) {
// Create a void pointer which "acts like" a string
void * string = "hello";
// "Cast" the string so it's easier to work with
char * string_cast = (char*) string;
// Print the string and a character in it
printf("The string is: %s\n", string_cast);
printf("The third character is: %c\n", string_cast[2]);
// How to now do something like:
// (1) void pointer_to_string_obj = ?
// (2) cast that pointer_to_string_obj to a normal string
// (3) print the string like it would normally be done
}
Could someone please show an example of manually creating a string pointer of type *(char**) and why that type would need to be created in the first place (why not just a normal char*?). I apologize if my question is broad, but basically I'm trying to figure out various void pointer types and where I'm at now in my very beginner understanding, it's a bit confusing, and so seeing a few examples would be very helpful.
So I thought up a kind of a good example of double void pointer (that is, void**). One way to cut down on double-free bugs is to always set pointers to NULL after freeing them.
We could do so like so (questionable style):
myprojectinclude.h:
/* must happen after any standard headers */
void freep(void **pointer);
#define free(p) error_call_freep_instead p /* so that free doesn't exist anymore */
freep.c:
#include <stdlib.h>
#include "myprojectinclude.h"
#undef free
void freep(void **p)
{
if (p) {
free(*p);
*p = NULL;
}
}
main.c:
#include <stdio.h>
#include <stdlib.h>
#include "myprojectinclude.h"
int main()
{
char *buffer = malloc(2048);
size_t buffer_size = 2048;
/* ... lots of code involving reading lines, etc. */
freep(&buffer);
/* buffer is guaranteed to be NULL here */
}
With this setup, double free is impossible. If we do
freep(&buffer);
freep(&buffer);
nothing goes wrong because buffer is NULL after the first call. (Note that passing NULL to free is safe; else we would add a NULL check like I had to do decades ago.)

Copying strings from extern char environ in C

I have a question pertaining to the extern char **environ. I'm trying to make a C program that counts the size of the environ list, copies it to an array of strings (array of array of chars), and then sorts it alphabetically with a bubble sort. It will print in name=value or value=name order depending on the format value.
I tried using strncpy to get the strings from environ to my new array, but the string values come out empty. I suspect I'm trying to use environ in a way I can't, so I'm looking for help. I've tried to look online for help, but this particular program is very limited. I cannot use system(), yet the only help I've found online tells me to make a program to make this system call. (This does not help).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char *argv[])
{
char **env = environ;
int i = 0;
int j = 0;
printf("Hello world!\n");
int listSZ = 0;
char temp[1024];
while(env[listSZ])
{
listSZ++;
}
printf("DEBUG: LIST SIZE = %d\n", listSZ);
char **list = malloc(listSZ * sizeof(char**));
char **sorted = malloc(listSZ * sizeof(char**));
for(i = 0; i < listSZ; i++)
{
list[i] = malloc(sizeof(env[i]) * sizeof(char)); // set the 2D Array strings to size 80, for good measure
sorted[i] = malloc(sizeof(env[i]) * sizeof(char));
}
while(env[i])
{
strncpy(list[i], env[i], sizeof(env[i]));
i++;
} // copy is empty???
for(i = 0; i < listSZ - 1; i++)
{
for(j = 0; j < sizeof(list[i]); j++)
{
if(list[i][j] > list[i+1][j])
{
strcpy(temp, list[i]);
strcpy(list[i], list[i+1]);
strcpy(list[i+1], temp);
j = sizeof(list[i]); // end loop, we resolved this specific entry
}
// else continue
}
}
This is my code, help is greatly appreciated. Why is this such a hard to find topic? Is it the lack of necessity?
EDIT: Pasted wrong code, this was a separate .c file on the same topic, but I started fresh on another file.
In a unix environment, the environment is a third parameter to main.
Try this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[], char **envp)
{
while (*envp) {
printf("%s\n", *envp);
*envp++;
}
}
There are multiple problems with your code, including:
Allocating the 'wrong' size for list and sorted (you multiply by sizeof(char **), but should be multiplying by sizeof(char *) because you're allocating an array of char *. This bug won't actually hurt you this time. Using sizeof(*list) avoids the problem.
Allocating the wrong size for the elements in list and sorted. You need to use strlen(env[i]) + 1 for the size, remembering to allow for the null that terminates the string.
You don't check the memory allocations.
Your string copying loop is using strncpy() and shouldn't (actually, you should seldom use strncpy()), not least because it is only copying 4 or 8 bytes of each environment variable (depending on whether you're on a 32-bit or 64-bit system), and it is not ensuring that they're null terminated strings (just one of the many reasons for not using strncpy().
Your outer loop of your 'sorting' code is OK; your inner loop is 100% bogus because you should be using the length of one or the other string, not the size of the pointer, and your comparisons are on single characters, but you're then using strcpy() where you simply need to move pointers around.
You allocate but don't use sorted.
You don't print the sorted environment to demonstrate that it is sorted.
Your code is missing the final }.
Here is some simple code that uses the standard C library qsort() function to do the sorting, and simulates POSIX strdup()
under the name dup_str() — you could use strdup() if you have POSIX available to you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
/* Can also be spelled strdup() and provided by the system */
static char *dup_str(const char *str)
{
size_t len = strlen(str) + 1;
char *dup = malloc(len);
if (dup != NULL)
memmove(dup, str, len);
return dup;
}
static int cmp_str(const void *v1, const void *v2)
{
const char *s1 = *(const char **)v1;
const char *s2 = *(const char **)v2;
return strcmp(s1, s2);
}
int main(void)
{
char **env = environ;
int listSZ;
for (listSZ = 0; env[listSZ] != NULL; listSZ++)
;
printf("DEBUG: Number of environment variables = %d\n", listSZ);
char **list = malloc(listSZ * sizeof(*list));
if (list == NULL)
{
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < listSZ; i++)
{
if ((list[i] = dup_str(env[i])) == NULL)
{
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
}
qsort(list, listSZ, sizeof(list[0]), cmp_str);
for (int i = 0; i < listSZ; i++)
printf("%2d: %s\n", i, list[i]);
return 0;
}
Other people pointed out that you can get at the environment via a third argument to main(), using the prototype int main(int argc, char **argv, char **envp). Note that Microsoft explicitly supports this. They're correct, but you can also get at the environment via environ, even in functions other than main(). The variable environ is unique amongst the global variables defined by POSIX in not being declared in any header file, so you must write the declaration yourself.
Note that the memory allocation is error checked and the error reported on standard error, not standard output.
Clearly, if you like writing and debugging sort algorithms, you can avoid using qsort(). Note that string comparisons need to be done using strcmp(), but you can't use strcmp() directly with qsort() when you're sorting an array of pointers because the argument types are wrong.
Part of the output for me was:
DEBUG: Number of environment variables = 51
0: Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.tQHOVHUgys/Render
1: BASH_ENV=/Users/jleffler/.bashrc
2: CDPATH=:/Users/jleffler:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work:/Users/jleffler/soq/src
3: CLICOLOR=1
4: DBDATE=Y4MD-
…
47: VISUAL=vim
48: XPC_FLAGS=0x0
49: XPC_SERVICE_NAME=0
50: _=./pe17
If you want to sort the values instead of the names, you have to do some harder work. You'd need to define what output you wish to see. There are multiple ways of handling that sort.
To get the environment variables, you need to declare main like this:
int main(int argc, char **argv, char **env);
The third parameter is the NULL-terminated list of environment variables. See:
#include <stdio.h>
int main(int argc, char **argv, char **environ)
{
for(size_t i = 0; env[i]; ++i)
puts(environ[i]);
return 0;
}
The output of this is:
LD_LIBRARY_PATH=/home/shaoran/opt/node-v6.9.4-linux-x64/lib:
LS_COLORS=rs=0:di=01;34:ln=01;36:m
...
Note also that sizeof(environ[i]) in your code does not get you the length of
the string, it gets you the size of a pointer, so
strncpy(list[i], environ[i], sizeof(environ[i]));
is wrong. Also the whole point of strncpy is to limit based on the destination,
not on the source, otherwise if the source is larger than the destination, you
will still overflow the buffer. The correct call would be
strncpy(list[i], environ[i], 80);
list[i][79] = 0;
Bare in mind that strncpy might not write the '\0'-terminating byte if the
destination is not large enough, so you have to make sure to terminate the
string. Also note that 79 characters might be too short for storing env variables. For example, my LS_COLORS variable
is huge, at least 1500 characters long. You might want to do your list[i] = malloc calls based based on strlen(environ[i])+1.
Another thing: your swapping
strcpy(temp, list[i]);
strcpy(list[i], list[i+1]);
strcpy(list[i+1], temp);
j = sizeof(list[i]);
works only if all list[i] point to memory of the same size. Since the list[i] are pointers, the cheaper way of swapping would be by
swapping the pointers instead:
char *tmp = list[i];
list[i] = list[i+1];
list[i+1] = tmp;
This is more efficient, is a O(1) operation and you don't have to worry if the
memory spaces are not of the same size.
What I don't get is, what do you intend with j = sizeof(list[i])? Not only
that sizeof(list[i]) returns you the size of a pointer (which will be constant
for all list[i]), why are you messing with the running variable j inside the
block? If you want to leave the loop, the do break. And you are looking for
strlen(list[i]): this will give you the length of the string.

Function that prints reverse of a string/char array in C

I am rather new to the C language right now and I am trying some practice on my own to help me understand how C works. The only other language I know proficiently is Java. Here is my code below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char * reverse(char word[]);
const char * reverse(char word[]) {
char reverse[sizeof(word)];
int i, j;
for (i = sizeof(word - 1); i <= 0; i--) {
for (j = 0; j > sizeof(word - 1); j++) {
reverse[i] = word[j];
}
}
return reverse;
}
int main() {
char word[100];
printf("Enter a word: ");
scanf("%s", word);
printf("%s backwards is %s\n", word, reverse(word));
return 0;
}
When the user enters a word, the program successfully prints it out when i store it but when i call the reverse function I made it doesnt return anything. It says on my editor the address of the memory stack is being returned instead and not the string of the array I am trying to create the reverse of in my function. Can anyone offer an explanation please :(
sizeof(word) is incorrect. When the word array is passed to a function, it is passed as a pointer to the first char, so you are taking the size of the pointer (presumably 4 or 8, on 32- or 64-bit machines). Confirm by printing out the size. You need to use strlen to get the length of a string.
There are other problems with the code. For instance, you shouldn't need a nested loop to reverse a string. And sizeof(word-1) is even worse than sizeof(word). And a loop that does i-- but compares i<=0 is doomed: i will just keep getting more negative.
There are multiple problems with your reverse function. C is very different from Java. It is a lot simpler and has less features.
Sizes of arrays and strings don't propagate through parameters like you think. Your sizeof will return wrong values.
reverse is an identifier that is used twice (as function name and local variable).
You cannot return variables that are allocated on stack, because this part of stack might be destroyed after the function call returns.
You don't need two nested loops to reverse a string and the logic is also wrong.
What you probably look for is the function strlen that is available in header string.h. It will tell you the length of a string. If you want to solve it your way, you will need to know how to allocate memory for a string (and how to free it).
If you want a function that reverses strings, you can operate directly on the parameter word. It is already allocated outside the reverse function, so it will not vanish.
If you just want to output the string backwards without really reversing it, you can also output char after char from the end of the string to start by iterating from strlen(word) - 1 to 0.
Edit: Changed my reverse() function to avoid pointer arithmetic and to allow reuse of word.
Don't return const values from a function; the return value cannot be assigned to, so const doesn't make sense. Caveat: due to differences between the C and C++ type system, you should return strings as const char * if you want the code to also compile as C++.
Arrays passed as params always "decay" to a pointer.
You can't return a function-local variable, unless you allocate it on the heap using malloc(). So we need to create it in main() and pass it as a param.
Since the args are pointers, with no size info, we need to tell the function the size of the array/string: sizeof won't work.
To be a valid C string, a pointer to or array of char must end with the string termination character \0.
Must put maximum length in scanf format specifier (%99s instead of plain %s — leave one space for the string termination character \0), otherwise vulnerable to buffer overflow.
#include <stdio.h> // size_t, scanf(), printf()
#include <string.h> // strlen()
// 1. // 2. // 3. // 4.
char *reverse(char *word, char *reversed_word, size_t size);
char *reverse(char *word, char *reversed_word, size_t size)
{
size_t index = 0;
reversed_word[size] = '\0'; // 5.
while (size-- > index) {
const char temp = word[index];
reversed_word[index++] = word[size];
reversed_word[size] = temp;
}
return reversed_word;
}
int main() {
char word[100];
size_t size = 0;
printf("Enter a word: ");
scanf("%99s", word); // 6.
size = strlen(word);
printf("%s backwards is %s\n", word, reverse(word, word, size));
return 0;
}

Can't initialize array of strings and use them

This is outside the main:
char message_ecran[NUMBER_OF_STRINGS][STRING_LENGTH+1];
And this is my function
int main(void)
{
Init();
int i;
char texte7[] = "io";
for (i=0;i<=NUMBER_OF_STRINGS;i++)
{
message_ecran[i] = texte7;
}
}
I would like to have an array of strings message_ecran, but it does'nt work:
incompatible types in assignment
strcpy() , implemented in your program.
#include <string.h>
#include <stdio.h>
#define NUMBER_OF_STRINGS 3
#define STRING_LENGTH 80
char message_ecran[NUMBER_OF_STRINGS][STRING_LENGTH+1];
int main(void)
{
int i;
char texte7[] = "io";
for (i=0;i<=NUMBER_OF_STRINGS;i++)
{
strcpy(message_ecran[i],texte7);
puts(message_ecran[i]);
}
}
You have to use strcpy for copying strings, assignment will not work.
Replace
message_ecran[i] = texte7;
with
strcpy(message_ecran[i], texte7);
The operation you're doing now is an assignment of the pointer.
You can't simply assign one string to another, you have to strcpy() to really copy data.
strcpy(message_ecran[i], texte7);
Also you have to be sure that there is enough memory allocated in message_ecran[i] for string that you're trying to copy. Otherwise, you will corrupt the data.
EDIT:
You can check, the following:
if(strlen(texte7) < STRING_LENGTH+1)
Or you can simply use the following function:
char * strncpy ( char * destination, const char * source, size_t num );
strncpy(message_ecran[i], texte7, STRING_LENGTH);
use strcpy(destination,source) and try

Resources