I'm sure this question has been asked before, but I've been searching pretty constantly for the last 2 days and can't solve this, so I'm hoping someone will help me out.
For my C assignment, we are supposed to receive each function parameter as a pointer. One of the values passed is a 2D char array, and for the last 48+ hours, I haven't been able to find out how to properly do this in my book, notes, instructor slides, etc. and actually make it work.
Simplified code of what I'm trying to accomplish
void main(){
/*
This is the value being passed. The first dimension has a size of 1000,
to account for 1000 different sorted people. Second dimension is an
array that holds the actual name, with a max name length of 25.
*/
char names[1000][25] = {'\0'};
read_files(names);
}
void read_files(char* names){
char newName1[4] = "mary";
char newName2[4] = "anna";
for(int i = 0; i < 5; i++){
names[0][i] = newName1[i];
}
for(int i = 0; i < 5; i++){
names[1][i] = newName2[i];
}
}
Basically, I'm trying to get names[0][x] to have x = "mary", and names[1][x] to have x = "anna".
(Or, more literally, names[0][0]= 'm', names[0][1]= 'a', etc.)
Unfortunately I can't get the passing right. The closest I've gotten is to have it assign one name to names, but not anymore.
Any help with this would be fantastic. There's actually quite a few classmates I'm working with who are all stumped on this. I'm sure its easy for experienced guys, but we just haven't gotten good instruction on how to do it and I can't find many specific examples that address this exact issue.
As I said, our instructor specifically wants function arguments to be pointers.
EDIT
Good info so far, I was very close to a few of these examples. I'll give them a shot and see what works.
You would have to define read_files as this:
void read_files(char (*names)[25]) {
char newName1[5] = "mary";
char newName2[5] = "anna";
for(int i = 0; i < 5; i++){
names[0][i] = newName1[i];
}
for(int i = 0; i < 5; i++){
names[1][i] = newName2[i];
}
}
Because you need to pass a pointer to an array of char[25]. A two
dimensional array cannot be converted into a double-pointer or a single pointer.
Note also that newName1 and newName2 must be arrays that can hold 5 elements. In C a
string is a sequence of characters that ends with the '\0'-terminating byte.
That means that for a string of length n, you need a char array of length
n+1, because you need the extra byte for the '\0'-terminating byte.
Also the better way of copying strings is strcpy or strncpy. strncpy is
more safe than strcpy because you limit the amount of bytes to be copied, thus
avoiding a buffer overflow. But strncpy might not write the '\0'-terminating byte
if there is not enough room.
Instead of:
for(int i = 0; i < 5; i++){
names[0][i] = newName1[i];
}
use this:
strcpy(names[0], newName1);
Or the strncpy way:
strncpy(names[0], newName1, sizeof names[0]);
names[0][sizeof(names[0]) - 1] = 0; // making sure to add the \0 byte
Also bear in mind, that the correct definition of the main function can be
only one of these:
int main(void);
int main(int argc, char **argv);
int main(int argc, char *argv[]);
#include <stdio.h>
void read_files(char* names){
char newName1[4] = "mary";
char newName2[4] = "anna";
for(int i = 0; i < 5; i++){
names[i] = newName1[i];
}
int offset = 25;
for(int i = 0; i < 5; i++){
names[i + offset] = newName2[i];
}
}
void main(){
/*
This is the value being passed. The first dimension has a size of 1000,
to account for 1000 different sorted people. Second dimension is an
array that holds the actual name, with a max name length of 25.
*/
char names[1000][25] = {'\0'};
read_files(names[0]);
printf("%s %s", names[0], names[1]);
}
In memory a 2d array is still a 1d block with fancy addressing. So you could have an offset variable and just add it on to every name like I have done here
Related
I came across this question and it asks if it's possible to write the function
void insert(char* M, char* T, int i)
that inserts the string T inside M starting from the index i, without using an intermediate string... I tried to use realloc but I think there's a problem when the original string M is a lot smaller than the result, my theory is that realloc changes the address of the string to be able to represent the new string.
For example: M="Wg" T="ron" and i=1; the result should be M="Wrong".
I'm using the following code:
void insert(char* M,char* T,int i)
{
int l;
l=strlen(M);
M=realloc(M,l+strlen(T)+1);
for(int j = l-1; j >= i; j--)
{
M[j+strlen(T)]=M[j];
}
for(int j = 0;j < strlen(T); j++)
{
M[i+j]=T[j];
}
M[l+strlen(T)]='\0'; //from what i've tested the string M is correct.
}
and using this declaration:
char *s=malloc(3);
char *c=malloc(18);
strcpy(s,"as");
strcpy(c,"bcdefghijklmnopqr");
insert(s,c,1); //this example does not work on my machine.
I hope this clarifies the question.
So is there a way to do it?
Example of a possible implementation using memmove. Explanations are in the comments
#include <stdio.h>
#include <string.h>
void insertString(char* M, const char* T, size_t index)
{
// ASSUMES there's enough space in M for this operation
// get the original lengths of each string
size_t Mlen = strlen(M);
size_t Tlen = strlen(T);
if (index < Mlen)
{
// M+index+Tlen is the destination position where the remaining characters in M will start
// M+index is the index where T will be inserted
// Mlen-index is the remaining number of characters in M that need to move
memmove(M+index+Tlen, M+index, Mlen-index);
// copy the T string to the space we just created
memcpy(M+index, T, Tlen);
// NUL terminate the new string
M[Mlen + Tlen] = '\0';
}
else
{
// simply strcat if the index falls outside the range of M
strcat(M, T);
}
}
If you're not allowed to use memmove or memcpy, it's simple enough to roll your own.
Demo
[This isn't really an answer; it's a clarification that's too complicated for a comment.]
If you can assume that the caller looks like
char string[6] = "Wg";
insert(string, "ron", 1);
(or with the string array having any size greater than 5), then you can write insert() easily.
If you can assume that the caller looks like
char *string = malloc(3);
strcpy(string, "Wg");
insert(string, "ron", 1);
then you can almost write insert() using realloc to make the string larger, except you have no way to return the possibly-new (that is, possibly moved) value of string.
If the caller might look like
char *string = "Wg";
insert(string, "ron", 1);
or even
char *string = "Wg\0\0\0";
insert(string, "ron", 1);
than you definitely cannot write insert(), because you cannot assume that the pointed-to string is writable (and on many platforms it will not be).
So, in general, the answer is: "No". You cannot write a general-purpose version of insert() that will work under all circumstances.
Note, too, that if you were to assume that the string were in malloc'ed memory and that you could use realloc (as in my second example), that code would not work for strings that were not malloc'ed (that is, it would not work for callers like my first example), and it would have no portable way of knowing, based on the pointer passed to it, whether it would be abe to safely use realloc or not.
I have one char strings pointers array, lets call it "str_array".
I also have 3 strings:
1."Hello"
2."World"
3."Today"
(Every string ends with \0)
And I have this function, that receives our str_array, the size of it, and another pointers arr that is not releveant for my question.
The problem that I encounter, is that function "scanString" receives NULL, or garbage values, instead of the strings inside str_array.
unsigned int RemoveFromStrArray(char*** str_array, unsigned int str_array_size, char** ptr_to_chars_array)
{
int k = 0;
while (k != str_array_size)
{
// This is the part where im trying to scan str_array strings.
scanString(*str_array[k]);
str_array++;
k++;
}
}
int scanString(char* string)
{
int c = 0;
int counter = 0;
while (string[c] != '\0')
{
if (string[c] == 1)
{
moveOneBack(string, c);
c--;
counter++;
}
c++;
}
return c;
}
I've been trying multiple alternative ways to scan str_array string arrays.
But all of my times I just had to deal with garbage values or NULL strings.
How do I reach str_array strings, that would be passed by reference to scanString?
Picture of what I'm talking about:
BIG THANKS IN ADVANCE!
unsigned int RemoveFromStrArray(char*** str_array
You've got at least one too many indirections there. From your drawing, str_array refers to an array, so it's effectively a pointer, and the values in the array are pointers to char. You don't seem to be changing the array itself, e.g. you're not making str_array refer to some other array of strings, so there's no need to pass the address of the array. So char ** is closer to the type you want.
str_array++;
I'm not sure why you're incrementing str_array and using k as an index into the array. Although it's safe to modify parameters locally like that, it's nice to avoid it so that you can refer to them while debugging. Consider this:
unsigned int RemoveFromStrArray(char** str_array,
unsigned int str_array_size)
{
int k = 0;
while (k != str_array_size)
{
char *string = str_array[k];
scanString(string);
k++;
}
}
That is, the values in str_array are of type char *, so str_array[k] has that type. Copying it into a temporary variable string makes it a little easier to see what's going on. (I removed the last parameter, ptr_to_chars_array, to keep things simple and because it's not used. You'll add it back if you have plans for it, I'm sure.)
A for loop would be a little more compact but otherwise equivalent:
unsigned int RemoveFromStrArray(char** str_array,
unsigned int str_array_size)
{
for (int k = 0; k < str_array_size; k++)
{
char *string = str_array[k];
scanString(string);
}
}
I'm trying to build a 2 dimensional array by using str.cpy, but the program fails. The code receives 4 arrays add copies their content to their matching arrays.
int InsertStudent(char *firstName, char* lastName, char* dynCourses, char *dynGrades,
char firstNames[50][20],
char familyNames[50][20], char courses[50][5][20],
char grades[50][5])
{
int set,
int cset = 0;
for (set = 0; set <= (50); set++)
{
if (firstNames[set][cset] == '\0')
{
strcpy(firstNames[set][cset], firstName);
strcpy(familyNames[set], lastName);
for (cset = 0; cset <= 5; cset++)
{
strcpy(courses[set], dynCourses);
strcpy(grades[set], dynGrades);
}
}
}
return 0;
}
Well clearly the error is using strcpy wrongly.
The correct way would be
strcpy(firstNames[set], firstName);
Also in the loop it should be
for (cset = 0; cset < MAX_COURSES; cset++)
{
strcpy(courses[cset], dynCourses);
strcpy(grades[cset], dynGrades);
}
Note that the idiomatic C loop is for (int i = 0; i < MAX; i++), using < and not <=.
The signature of the strcpy function is
char *strcpy(char * restrict s1, const char * restrict s2);
Earlier you passed in place of s1 a char instead of char*. You must have got some warning (if enabled). If not then turn all compiler flags -Wall -Werror.
if (firstNames[set][cset] == '\0')
But if you are initially checking an uninitilized value with \0. This will higly unlikely will turn out to be false. There is no gurantee that the char array which doesn't contain strings will be initialized with 0 automatically. So make sure you have initialized the char arrays in the callee function like this
char arr[20][50]={0};
The loop is from 0 to MAX_STUDENTS. You are invoking an undefined behavior looping on array index out of bound if MAX_STUDENTS is greater than or equal to 50. Same goes for MAX_COURSES. More clearly the looping would be for (set = 0; set < (50); set++).
Again there will be a lot better way to deal with it if you put the initialization and copy part seperate. Otherwise this will be uneasy to maintain.
Seeing your use courses it is obvious that you want to declare it like this
char courses[5][20];
same goes for the grades array. Here you were trying to copy a string into into a 2d array. Compiler would complain about type incompatibility.
Also in the function you didn't return anything meaningful. The correct way would be to return the index value on which new name or information is added.
Here you are copying content of dynGrades and dynCourses to the array. So they will all contain the same values. Is this what you want? Because then what's the use of keeping 5 seperate char arrays - one could serve the purpose pretty well.
Basically I have this working code that manipulates an array of strings:
for (i = 0; i < numentries; i++)
if (strcmp(compare_str, strings[i]) < 0)
break;
for (j = numentries; j > i; j--)
strcpy(strings[j], strings[j - 1]);
strcpy(strings[i], compare_str);
strcat(strings[i], " ");
strcat(strings[i], whole_str);
numentries++;
I want to make it so I can call to a method to do the manipulation like:
//call to method
compare(strings, numentries, compare_str, whole_str);
numentries++;
//method
void compare(char array[], int entries, char compare[], char whole[]) {
int i, j;
for (i = 0; i < entries; i++)
if (strcmp(compare, array[i]) < 0)
break;
for (j = entries; j > i; j--)
strcpy(array[j], array[j - 1]);
strcpy(array[i], compare);
strcat(array[i], " ");
strcat(array[i], whole);
}
The above code doesn't work, I know you have to use pointers some how much im not exactly sure how. How do I pass my array of characters (strings) so I can manipulate the values inside the array?
I know this is only a code snippet, but I thought it would be enough to solve my problem. if you need more of my program to help me let me know.
It crashes at the line
strcat(array[i], " ");
with the message
Program received signal SIGSEGV, Segmentation fault. 0x75bf8df6 in strcat () from C:\Windows\system32\msvcrt.dll n Single stepping until exit from function strcat, which has no line number information. 0x77776299 in ntdll!LdrGetDllHandleByName () from C:\Windows\system32\ntdll.dll
void compare(char *array[], int entries, char compare[], char whole[]) {
^
Proof: http://ideone.com/6xSaq
I would just use a double pointer (pointer to a pointer). In fact I would just use pointers instead of arrays. Eventually they all boil down to the same thing anyway.
void compare(char **array, int entries, char *compare, char *whole) {
I don't think any of the body needs to be modified.
EDIT:
Also the segmentation fault you are seeing in your code is probably occuring because of insufficient storage space. If you're using malloc for allocation use realloc to allocate enough space for the string concatenation. If you're using stack storage, then allocate enough space for every element of the array, incase you need to perform concatenations.
Given the declaration char strings[30][161];, I think you need the function to be
void compare(char array[][161], int entries, char compare[], char whole[])
because you are not passing an array of pointers to chars, you are passing an 2D array of chars. C needs to know the inner dimension of 2D arrays to do indexing correctly. See this article for details
The alternative to hardcoding sizes would be to add something like this before the function call:
char* stringPtrs[N_STRINGS];
for (i=0;i<N_STRINGS;i++) { stringPtrs[i]=strings[i]; }
compare(stringPtrs, entries, ...);
Ok, this is a C programming homework question. But I'm truly stuck.
I ask the user to input words, and then I insert the input into an array, but I can't have any control over the number of words the user types.
I guess what I'm asking is how do you declare a an array in C without declaring its length and without asking the user what the length should be.
I know this has something to do with malloc, but if you could give me some examples of how to do this, I would really appreciate it.
You can malloc a block of memory large enough to hold a certain number of array items.
Then, before you exceed that number, you can use realloc to make the memory block bigger.
Here's a bit of C code that shows this in action, reallocating an integer array whenever it's too small to hold the next integer.
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int *xyzzy = NULL; // Initially NULL so first realloc is a malloc.
int currsz = 0; // Current capacity.
int i;
// Add ten integers.
for (i = 0; i < 10; i++) {
// If this one will exceed capacity.
if (i >= currsz) {
// Increase capacity by four and re-allocate.
currsz += 4;
xyzzy = realloc (xyzzy, sizeof(int) * currsz);
// Should really check for failure here.
}
// Store number.
xyzzy[i] = 100 + i;
}
// Output capacity and values.
printf ("CurrSz = %d, values =", currsz);
for (i = 0; i < 10; i++) {
printf (" %d", xyzzy[i]);
}
printf ("\n");
return 0;
}
You can realloc it every time like:
int size = 0;
char **array = malloc(0);
while(/* something */)
{
char *string = // get input
size++;
array = realloc(array, size * sizeof(char*));
array[size - 1] = string;
}
Or in chunks if you care about speed.
Yes, you want malloc. Checkout this tut.
http://www.cprogramming.com/tutorial/dynamic_memory_allocation.html
This site is good in general for learning.
Here is an example of using realloc, it is basically exactly what you are asking to do.
http://www.cplusplus.com/reference/clibrary/cstdlib/realloc/
0) obviously you will need multiple buffers, so you will need a list like structure: perhaps a record with char array 100 chars and a pointer to next structure
1) You need to capture the words char by char and store them in your buffer
2) once the buffer is full you allocate another record, chain it with the previous one and keep going until you are out of mem or the process is over.
That should be better performance than realloc function. I believe malloc is trying to give contious block of memory. Therefore the list like structure will be faster and work better.