I'm pretty new to C, but have programmed a great deal in both Java and Python. So I got the basics going – however, C keeps hitting me with this Segmentation fault: 11 no matter how I wrap my head around my code. I suspect the problem may be that I'm accessing beyond the bounds of my array, namely in this part:
ordListe[i] = (char*)malloc(n);
strcpy(ordListe[i], ord);
For hours I've been at a loss how to solve this, so now I turn to you: what am I missing? I couldn't find many posts involving pointers to pointers out there with a similar problem, so i assume this will be of some help to others in the future too. Full function where the problem occurs:
char** split(char* s)
{
char* setning = (char*) malloc(strlen(s) + 1);
strcpy(setning, s); //Setning blir det samme som s, saa vi slipper aa roere s
printf("%s\n", setning); //TNB: Feilsoeking
char* ord = (char *) malloc(strlen(setning) * sizeof(char)); //Henter ut ordet
ord = strtok(setning, " "); //Henter foerste ord
printf("%s", ord);
int i = 0;
size_t n = 0;
char** ordListe = (char**) malloc(strlen(s) * sizeof(char)); //Lager en liste vi kan lagre verdiene i
ordListe[strlen(setning)+1] = NULL;
while(ord != NULL)
{
n = strlen(ordListe[i]) + 1;
ordListe[i] = (char*)malloc(n);
strcpy(ordListe[i], ord);
i++;
ord = strtok (NULL, " "); //Beveger den videre
}
free(setning);
return ordListe;
}
The purpose of the function is to split a string and store the individual parts in a pointer-to-pointer array and return said array.
Thanks in advance!
Here is one way to do it:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/****************************************************************
**
*/
char **split(const char *s)
{
char *setning = NULL;
char *ord = NULL;
char **ordListe = NULL;
int elements = 0;
char **new;
/* Build non-const version of the input string. */
errno=0;
setning = strdup(s);
if(!setning)
{
fprintf(stderr, "strdup() reports: %d %s\n", errno, strerror(errno));
goto CLEANUP;
}
/* Parse the string into an array of strings. */
ord = strtok(setning, " ");
while(ord)
{
/* Increase the array of string pointers by one. */
errno=0;
new=realloc(ordListe, sizeof(char *) * (elements + 1));
if(!new)
{
fprintf(stderr, "realloc() reports: %d %s\n", errno, strerror(errno));
goto CLEANUP;
}
ordListe=new;
/* Append an allocated string to the end of the array of strings. */
ordListe[elements] = strdup(ord);
if(!ordListe[elements])
{
fprintf(stderr, "strdup() reports: %d %s\n", errno, strerror(errno));
goto CLEANUP;
}
++elements;
ord = strtok(NULL, " ");
}
/* Add a NULL termination value to the end of the array of strings. */
errno=0;
new=realloc(ordListe, sizeof(char *) * (elements + 1));
if(!new)
{
fprintf(stderr, "realloc() reports: %d %s\n", errno, strerror(errno));
goto CLEANUP;
}
ordListe=new;
ordListe[elements] = NULL;
CLEANUP:
/* Free the working copy of the string. */
if(setning)
free(setning);
/* Free everything if there was an error, and return NULL. */
if(errno)
{
while(elements)
free(ordListe[elements--]);
free(ordListe);
ordListe=NULL;
}
return ordListe;
}
/****************************************************************
** Program start.
*/
int main(int argC, char *argV[])
{
char **ordListe = NULL;
char **iter = NULL;
/* Split a string. */
ordListe=split("Now is the time for all good men to come to the aid of their country.");
if(!ordListe)
{
fprintf(stderr, "split() failed.\n");
goto CLEANUP;
}
/* Print each split-out string. */
iter = ordListe;
while(*iter)
{
printf("%s\n", *iter);
++iter;
}
CLEANUP:
/* Free the array of strings. */
if(ordListe)
{
iter = ordListe;
while(*iter)
{
free(*iter);
++iter;
}
free(ordListe);
}
return(0);
}
Related
I need split data from ethernet. Data is in this format:
ZMXXX,angle*CHCK
Where angle is number. For example: ZMXXX,900*5A
And I need separated ZMXXX,900 and 5A. I wrote this function:
void split_data(char analyze[])
{
char *words[5]; uint8_t i=0;
words[i] = strtok(analyze,"*");
while(words[i]!=NULL)
{
words[++i] = strtok(NULL,"*");
}
}
And result is here:
And now, how I can get this data from variable:
words[0]
words[1]
Assuming the format you mention to be fixed, there is no need for the expensive and error-prone strtok().
Use the good old strchr():
int parse(char * input, char ** output)
{
int result = -1; /* Be pessimistic. */
if ((NULL == inout) || (NULL == output))
{
errno = EINVAL;
goto lblExit;
}
char * pc = strchr(analyze, '*');
if (NULL == pc);
{
errno = EINVAL;
goto lblExit;
}
*pc = '\0'; /* Set a temporary `0`-terminator. */
output[0] = strdup(analyze); /* Copy the 1st token. */
if (NULL == output[0])
{
goto lblExit;
}
*pc = '*'; /* Restore the original. */
output[1] = strdup(pc + 1); /* Seek beyond the `*` and copy the 2nd token. */
if (NULL == output[1])
{
free(outout[0]); /** Clean up. */
goto lblExit;
}
result = 0; /* Indicate success. */
lblExit:
return result;
}
Use it like this:
#define _POSIX_C_SOURCE 200809L /* To make strdup() available. */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int parse(char *, char **);
int main(void)
{
char data[] = "ZMXXX,900*5A";
char * words[2];
if (-1 == parse(data, words))
{
perror("parse() failed");
exit(EXIT_FAILURE);
}
printf("word 1 = '%s'\n", words[0]);
printf("word 2 = '%s'\n", words[1]);
free(words[0]);
free(words[1]);
return EXIT_SUCCESS;
}
The above code is expected to print:
word 1 = 'ZMXXX,900'
word 2 = '5A'
Note that strdup() isn't Standard C, but POSIX. It might need to be activated using one of the appropriate defines.
I'm writing a program to parse a command-line argument into three different parts: host name, file path, and file name, however I am unsure of how to parse a single command-line argument and store the separate parts in three different variables.
I need each portion to create a socket on the client-side of my program. So far I've been able to parse the host name portion, but I get stuck after that.
Is there a way that, after parsing a portion of the string?
EDIT:
The string I'm trying to parse is something like camelot.cba.csuohio.edu/~yourloginid/filename.txt
Here's my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int i, sk;
char buf[256], temp[256];
struct sockaddr_in remote;
struct hostent *hp;
if(argc != 2)
{
printf("Invalid number of arguments. Program terminating...");
exit(1);
}
sk = socket(AF_INET, SOCK_STREAM, 0);
remote.sin_family = AF_INET;
strcpy(buf, argv[1]);
for(i = 0; i < strlen(buf); i++)
{
if(buf[i] == '/')
break;
temp[i] = buf[i];
}
hp = gethostbyname(temp);
return 0;
}
EDIT:
I've implemented a while loop to achieve what I'm looking for, but I feel like it's sloppy. Is there a way I can improve it?
while(tk != NULL)
{
if(c == 0)
strcpy(host, tk);
else if(c == 1)
strcpy(path, tk);
else
strcpy(fname, tk);
c++;
tk = strtok(NULL, "/");
}
char st[] = "camelot.cba.csuohio.edu/~yourloginid/filename.txt";
char *host, *path, *fname;
char *ch[3];
for (int i=0; i < 3; ++i) {
ch[i] = strtok(st, "/");
(if ch[i] == NULL) break;
printf("%s\n", ch[i]);
}
if (ch[0] != NULL) {
host = ch[0];
}
if (ch[1] != NULL) {
path = ch[1];
}
if (ch[2] != null) {
path = ch[2];
}
Output:
camelot.cba.csuohio.edu
~yourloginid
filename.txt
You can parse that with strtok
A rough example for you case would be
const char s[2] = "/";
char *token;
/* get the first token */
token = strtok(argv[1], s);
/* walk through other tokens */
while( token != NULL )
{
printf( " %s\n", token );
token = strtok(NULL, s);
}
I didn't compile it but I hope you can use it as an example.
Here you have a complete example of how to use it
http://www.tutorialspoint.com/c_standard_library/c_function_strtok.htm
Hope this helps.
When you know the delimiters, never forget you have simple pointer arithmetic available to you to split/parse any sting. strtok and sscanf are fine tools, but you can do the same thing manually. Here is a short example to add to your list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXS 128
int main (int argc, char **argv) {
if (argc < 2 ) {
fprintf (stderr, "Error: insufficient input, usage: %s host,path,file\n", argv[0]);
return 1;
}
char *line = strdup (argv[1]); /* make a copy of argument string */
if (!line) {
fprintf (stderr, "error: strdup memory allocation/copy failed.\n");
return 1;
}
char *p = line; /* pointer to the argument string */
char *sp = NULL; /* pointer to use as start pointer */
char host[MAXS] = {0}; /* variables to hold tokens */
char path[MAXS] = {0};
char file[MAXS] = {0};
while (*p && *p != ',') p++; /* find the first ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (host, line); /* read/copy host name */
sp = p; /* set start pointer at current pos */
while (*p && *p != ',') p++; /* find next ',' */
*p++ = 0; /* null-terminate, advance pointer */
strcpy (path, sp); /* read/copy path */
strcpy (file, p); /* pointer on file, read/copy file */
printf ("\n host: %s\n path: %s\n file: %s\n\n", host, path, file);
free (line); /* free memory allocate by strdup */
return 0;
}
Output
$ ./bin/split_host_path_file hostname,pathname,filename
host: hostname
path: pathname
file: filename
Updated to prevent potential read beyond end of line with p.
you can also parse with strtok_r as follows, since strtok is not thread safe.
const char *delim="/";
char *str, *savePtr;
char hosts[3][32];
int i;
for(i=0,str=strtok_r(argv[1], delim, &savePtr);(str!=NULL);str=strtok_r(NULL, delim, &savePtr), i++)
{
print("%s\n", str);
strcpy((char *)host[i], (const char *)str);
}
access host array elements, as it will contain the indexed values delimited by "/"
So i am attempting to pass a string array (char** arguments) to a function, fill the array with values and then print those values after returning from the function. The problem occurs when I try to print the first value of "arguments" which gives me a segmentation fault. Why is this? when I print the values in the "getArguments" function all goes as expected. I am new to C and yes this is an assignment. I am not looking for you to write this code for me however I would like an explanation of this behaviour as I try to understand this concept.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFFERSIZE 81
int getArguments(char** arguments, char* argument);
void getPath(char* pathBuffer);
int checkForDirectoryChange(char **arguments, int num_args);
int main(int argc, char *argv[]){
char * command;
char ** arguments = NULL;
char * cd_path;
int len, pid, ret_code, cd_requested = 1;
char buffer[BUFFERSIZE];
/* Get user input and the first token */
printf("Enter a command: > ");
command = fgets(buffer,BUFFERSIZE,stdin);
printf("The command entered was %s",buffer);
len = strlen(buffer);
if(buffer[len-1] == '\n')
buffer[len-1]='\0';
cd_requested = getArguments(arguments, command);
printf("The argument passed is now: %s\n", arguments[0]);
if(cd_requested == 0){
fprintf(stdout,"Change directory requested.\n");
}
/*
char * pathBuf;
getPath(pathBuf);
free the memory allocated */
/*
pid = fork();
if(pid){
wait(NULL);
}else{
ret_code = execvp(*arguments, arguments);
if(ret_code){
printf("The fork failed, exiting.");
exit(0);
}
}*/
}
int getArguments(char** arguments, char* command){
int n_spaces = 0,i;
char *token;
token = strtok(command, " ");
/* Loop until we have gotten all of the tokens */
while (token) {
arguments = realloc (arguments, sizeof (char*) * ++n_spaces);
if (arguments == NULL){
printf("Memory allocation failed: token - %d\n", n_spaces);
exit (-1); /* memory allocation failed */
}
arguments[n_spaces-1] = token;
token = strtok (NULL, " ");
}
/* realloc one extra element for the last NULL */
arguments = realloc (arguments, sizeof (char*) * (n_spaces+1));
arguments[n_spaces] = 0;
/* print the result */
for (i = 0; i < (n_spaces+1); ++i)
printf ("arguments[%d] = %s\n", i, arguments[i]);
return strcmp("cd",arguments[0]);
}
int checkForDirectoryChange(char** arguments, int num_args){
return 0;
}
void getPath(char* pathBuffer){
size_t n;
n = confstr(_CS_PATH, NULL, (size_t) 0);
pathBuffer = malloc(n);
if (pathBuffer == NULL)
abort();
confstr(_CS_PATH, pathBuffer, n);
}
It is because getArguments() only reassigned the copy of pointer to pointer of characters inside itself. arguments in main() was not updated.
You should define getArguments() as
int getArguments(char*** arguments, char* command) {
/* ... */
while (token) {
*arguments = realloc (*arguments, sizeof (char*) * ++n_spaces);
if (*arguments == NULL){
printf("Memory allocation failed: token - %d\n", n_spaces);
exit (-1); /* memory allocation failed */
}
(*arguments)[n_spaces-1] = token;
token = strtok (NULL, " ");
}
/* ... */
}
And call it as the following inside main().
cd_requested = getArguments(&arguments, command);
H i am building a basic shell in c and i need to know the size of the array i am populating with user input. Here is the code.
/*
* Tp1.c
*
* Created on: 25 janv. 2014
* Author: shong
*/
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void cd_handler(int argc, char *argv[]);
int lire(char *chaine, int longueur);
char** init_command(char *str);
int main(int argc, char *argv[]) {
//printf("La valeur de argc est: %d", argc);
while(1){
printf("Log710H2014%>");
char str[200];
lire(str, 200);
char** comms = init_command(str);
printf("%s", comms[1]);
if(strcmp(comms[0], "cd") == 0){
int commArgsC = sizeof(comms)/sizeof(comms[0]);
cd_handler(commArgsC, comms);
}else if (strcmp(comms[0], "exit") == 0){
exit(0);
}
}
}
}
void cd_handler(int argc, char *argv[]){
char cwd[256];
char * directory;
if(argc < 2){
directory = getenv("HOME");
}else if (argc == 2){
directory = argv[1];
}else{
exit(1);
}
if (chdir(directory) == -1) {
printf ("chdir failed - %s\n", strerror (errno));
}else{
if (getcwd(cwd, sizeof(cwd)) == NULL)
perror("getcwd() error");
else
printf("current working directory is: %s\n", cwd);
}
}
char** init_command(char* str){
char ** res = NULL;
char * p = strtok (str, " ");
int n_spaces = 0, i;
while (p) {
res = realloc (res, sizeof (char*) * ++n_spaces);
if (res == NULL){
exit (-1);
}
res[n_spaces-1] = p;
p = strtok (NULL, " ");
}
res = realloc (res, sizeof (char*) * (n_spaces+1));
res[n_spaces] = 0;
//print the result
//for (i = 0; i < (n_spaces+1); ++i)
//printf ("res[%d] = %s\n", i, res[i]);
//free the memory allocated
//free (res);
return res;
}
int lire(char *chaine, int longueur)
{
char *positionEntree = NULL;
if (fgets(chaine, longueur, stdin) != NULL)
{
positionEntree = strchr(chaine, '\n');
if (positionEntree != NULL)
{
//*positionEntree = '\0'; // On remplace ce caractère par \0
}
return 1;
}
else
{
return 0; // on renvoie 0 s'il y a eu une erreur
}
}
The problem is that sizeof(comms) always return 8, no matter the number of elements in comm.
comms is a pointer, so on a 64-bit machine it will have a size of 8 bytes. C has no knowledge about the size of what it points to. You'll have to return the size from the function that allocates the storage and keep track of it yourself.
The behavior sizeof is dependent on what type of variable it is applied to.
If the variable is a pointer, as in the question, sizeof simply evaluates to the size of the pointer type in bytes:
int *y; //y points to an int... maybe an array? Who knows?
printf("%d",sizeof(y)); //No idea how y has been allocated. Defaults to sizeof(int*)
If the variable was declared as an array, sizeof returns the size of the entire array. For instance:
int y[4]; //y is exactly four ints in memory
printf("%d",sizeof(y)); //sizeof knows this, and evaluates to sizeof(int)*4
This is why the sizeof(table)/sizeof(table[0]) would work for an array. However, it does not work pointers as demonstrated above. In short, passing an array as an argument destroys any information regarding how much data is in that array, and you must pass the size separately. This is referred to as "array decay."
The difference between pointers and arrays is very subtle. Most of the time, the two can be used interchangeably, but there are two critical differences:
The difference in the behavior of sizeof as discussed previously.
Arrays cannot be assigned as pointers can. This relates to the fact that they are of constant size. For instance:
char **table;
//table can be assigned different values....
table = NULL;
//...multiples times, if wanted
table = malloc(sizeof(char*)*20);
However,
//table is constant
char *table[20];
//it's individual elements can be assigned as usual...
table[0] = malloc(1);
//...but attempts to change where table points to will fail
table = NULL; //This will cause a compilation error.
I need help with debugging this piece of code. I know the problem is in malloc and free but can't find exactly where, why and how to fix it. Please don't answer: "Use gdb" and that's it. I would use gdb to debug it, but I still don't know much about it and am still learning it, and would like to have, in the meanwhile, another solution.
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#define MAX_COMMAND_LENGTH 256
#define MAX_ARGS_NUMBER 128
#define MAX_HISTORY_NUMBER 100
#define PROMPT ">>> "
int num_elems;
typedef enum {false, true} bool;
typedef struct {
char **arg;
char *infile;
char *outfile;
int background;
} Command_Info;
int parse_cmd(char *cmd_line, Command_Info *cmd_info)
{
char *arg;
char *args[MAX_ARGS_NUMBER];
int i = 0;
arg = strtok(cmd_line, " ");
while (arg != NULL) {
args[i] = arg;
arg = strtok(NULL, " ");
i++;
}
num_elems = i;precisa em free_mem
if (num_elems == 0)
return 0;
cmd_info->arg = (char **) ( malloc(num_elems * sizeof(char *)) );
cmd_info->infile = NULL;
cmd_info->outfile = NULL;
cmd_info->background = 0;
bool b_infile = false;
bool b_outfile = false;
int iarg = 0;
for (i = 0; i < num_elems; i++)
{
if ( !strcmp(args[i], "<") )
{
if ( b_infile || i == num_elems-1 || !strcmp(args[i+1], "<") || !strcmp(args[i+1], ">") || !strcmp(args[i+1], "&") )
return -1;
i++;
cmd_info->infile = malloc(strlen(args[i]) * sizeof(char));
strcpy(cmd_info->infile, args[i]);
b_infile = true;
}
else if (!strcmp(args[i], ">"))
{
if ( b_outfile || i == num_elems-1 || !strcmp(args[i+1], ">") || !strcmp(args[i+1], "<") || !strcmp(args[i+1], "&") )
return -1;
i++;
cmd_info->outfile = malloc(strlen(args[i]) * sizeof(char));
strcpy(cmd_info->outfile, args[i]);
b_outfile = true;
}
else if (!strcmp(args[i], "&"))
{
if ( i == 0 || i != num_elems-1 || cmd_info->background )
return -1;
cmd_info->background = true;
}
else
{
cmd_info->arg[iarg] = malloc(strlen(args[i]) * sizeof(char));
strcpy(cmd_info->arg[iarg], args[i]);
iarg++;
}
}
cmd_info->arg[iarg] = NULL;
return 0;
}
void print_cmd(Command_Info *cmd_info)
{
int i;
for (i = 0; cmd_info->arg[i] != NULL; i++)
printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]);
printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]);
printf("infile=\"%s\"\n", cmd_info->infile);
printf("outfile=\"%s\"\n", cmd_info->outfile);
printf("background=\"%d\"\n", cmd_info->background);
}
void get_cmd(char* str)
{
fgets(str, MAX_COMMAND_LENGTH, stdin);
str[strlen(str)-1] = '\0';
}
pid_t exec_simple(Command_Info *cmd_info)
{
pid_t pid = fork();
if (pid < 0)
{
perror("Fork Error");
return -1;
}
if (pid == 0)
{
if ( (execvp(cmd_info->arg[0], cmd_info->arg)) == -1)
{
perror(cmd_info->arg[0]);
exit(1);
}
}
return pid;
}
void type_prompt(void)
{
printf("%s", PROMPT);
}
void syntax_error(void)
{
printf("msh syntax error\n");
}
void free_mem(Command_Info *cmd_info)
{
int i;
for (i = 0; cmd_info->arg[i] != NULL; i++)
free(cmd_info->arg[i]);
free(cmd_info->arg);
free(cmd_info->infile);
free(cmd_info->outfile);
}
int main(int argc, char* argv[])
{
char cmd_line[MAX_COMMAND_LENGTH];
Command_Info cmd_info;
//char* history[MAX_HISTORY_NUMBER];
while (true)
{
type_prompt();
get_cmd(cmd_line);
if ( parse_cmd(cmd_line, &cmd_info) == -1)
{
syntax_error();
continue;
}
if (!strcmp(cmd_line, ""))
continue;
if (!strcmp(cmd_info.arg[0], "exit"))
exit(0);
pid_t pid = exec_simple(&cmd_info);
waitpid(pid, NULL, 0);
free_mem(&cmd_info);
}
return 0;
}
Since strings in C are null-terminated, their actuall size in memory is length+1, so instead of
cmd_info->infile = malloc(strlen(args[i]) * sizeof(char));
You should have
cmd_info->infile = malloc((strlen(args[i])+1) * sizeof(char));
EDIT: As Aeth said, you need to change every single occurence of malloc to contain space for that additional null character:
cmd_info->arg = (char **) ( malloc(num_elems * sizeof(char *)) ); //this one can stay, since it determines number of strings, not string length
cmd_info->outfile = malloc((strlen(args[i])+1) * sizeof(char));
cmd_info->arg[iarg] = malloc((strlen(args[i])+1) * sizeof(char));
You need to allocate an extra char for each of your strings to handle the terminating null.
cmd_info->arg[iarg] = malloc((strlen(args[i])+1) * sizeof(char));
You need to allocate an additional char* in the cmd_info->arg array. This extra element will store the NULL that signifies the end of the array of arguments.
cmd_info->arg = (char **) ( malloc((num_elems+1) * sizeof(char *)) );
I have confirmed on my system that the program successfully frees all its memory without error after both of the changes listed were made.
When you are dynamically allocating memory for cmd_info->infile as:
cmd_info->infile = malloc(strlen(args[i]) * sizeof(char));
you are not allocating space for the terminating null char.
Same is the case with allocation for cmd_info->outfile
When you allocate space for n char and copy a string of length n into it, I think that overwrites the metadata that malloc maintains at the end of the array and this bug shows up when you call free to deallocate the memory as free does not find that meta data.
EDIT:
Change:
num_elems = i;
to
num_elems = i+1;
Since you are marking the end of the arguments with NULL
cmd_info->arg[iarg] = NULL;
you need to allocate the space for this.
In general, this error is usually the result of something writing data outside a malloc()'d block (off the end or before the beginning). This can corrupt the memory allocator's internal bookkeeping structures.
Others have already pointed out the particular problem in your code. In cases where it's more deeply hidden, I have found Valgrind to be useful for debugging. At the expense of noticable code slowdown, it is able to detect illegal memory accesses (in the form of "invalid reads" and "invalid writes") at a very fine-grained level. Memory debuggers such as dmalloc can also be helpful, and don't impose nearly as much overhead, but in my experience aren't quite as good as Valgrind for finding everything.
Valgrind, in its 'memcheck' mode, will output memory access errors with a stack trace of where in the program they occurred. Usually, when I have an "invalid pointer" error in free(), it will be preceeded at some point by an invalid write which memcheck will find.