dynamic allocation - pointer to pointer in c - c

i got exercise from school.
I need to write a function that get array of grades, the size of the array, int number that represent the range of the grades, array that represent the count of the number of the grades in the same range and array that sum the grade of in the range.
the function will create sub groups to the grades, for exmaple: if the range of the grades is 10 so that sub groups is: 0-9, 10-19, 20-29, 30-39,...,90-99,100.
the range is 10 so the number of the sub-groups is 100/range + 1 (11 in that example which the range is 10).
the function will return array that is pointer to pointer, each element in this array will represent the sub-group by ASC.
if there is no values in the range (sub-group) we need to set NULL.
the count array will count the number of the grade in the same range,
and the sum array will sum those grades.
we can assume that the size of those arrays are also 100/range + 1.
important: i need to use dynamic allocate memroy.
i think i have the problem to calculate to which index in the range i need to put the grade, it work in some cases, but not to all.
examples:
int grades[SIZE_GRADES] = {98,45,77,65,89,90,100,43,54,67,55,88,76,67,33,23,45,76,72,76};
int avg_grd1[100 / GRD_RANGE1 + 1];
int avg_grd2[100 / GRD_RANGE2 + 1];
int count_grd1[100 / GRD_RANGE1 + 1];
int count_grd2[100 / GRD_RANGE2 + 1];
int** arr1 = GradeStat(grades, SIZE_GRADES, GRD_RANGE1, count_grd1, avg_grd1);
int** arr2 = GradeStat(grades, SIZE_GRADES, GRD_RANGE2, count_grd2, avg_grd2);
#define GRD_RANGE1 10
#define GRD_RANGE2 7
#define SIZE_GRADES 20
those are working for me.
but what i insert this arrays it didn't: (same vars, just different array).
int grades[SIZE_GRADES] = { 45,46,58,68,70,73,74,78,90,94 };
#define GRD_RANGE1 10
#define GRD_RANGE2 15
#define SIZE_GRADES 10
and that's my code:
int** GradeStat(int* Grades, int size_grades, int grd_range, int* count_grd, int* avg_grd)
{
int sub_range = 100 / grd_range + 1;
int** class_grd;
int place_in_sub_range;
// alocate memory.
class_grd = (int**)calloc(sub_range, sizeof(int));
if (class_grd == NULL) {
exit(1);
}
for (int i = 0; i < sub_range; i++) {
count_grd[i] = 0;
}
for (int i = 0; i < sub_range; i++) {
avg_grd[i] = 0;
}
for (int i = 0; i < SIZE_GRADES; i++) {
// the index (place) in the pointer of pointer.
place_in_sub_range = Grades[i] / grd_range;
if (place_in_sub_range <= sub_range) {
// create new pointer that relevant to the range.
if (class_grd[place_in_sub_range] == 0) {
// alocate memory, set the grade and set the address of the pointer inside the pointer to pointer array.
int* sub_range_pointer = (int*)calloc(1, sizeof(int));
if (sub_range_pointer == NULL) {
exit(1);
}
// save the grade in the memory.
*sub_range_pointer = Grades[i];
// set the pointer address in the poinet of pointer array.
class_grd[place_in_sub_range] = sub_range_pointer;
// count the number of grades in each range.
count_grd[place_in_sub_range]++;
// set the grade in the sum array.
avg_grd[place_in_sub_range] = Grades[i];
}
else {
// find out how many elemnts is exist, realloc + add new grade and add the grade to there.
int* sub_range_pointer = (int*)realloc(class_grd[place_in_sub_range], count_grd[place_in_sub_range] * sizeof(int) + sizeof(int));
if (sub_range_pointer == NULL) {
exit(1);
}
// set the grade in the relevant range.
sub_range_pointer[count_grd[place_in_sub_range]] = Grades[i];
// set the pointer address in the poinet of pointer array.
class_grd[place_in_sub_range] = sub_range_pointer;
// raise the number of grades in the relevant range.
count_grd[place_in_sub_range]++;
// set the grade in the sum array.
avg_grd[place_in_sub_range] = (avg_grd[place_in_sub_range] + Grades[i]) / 2;
}
}
//else {
// return NULL;
//}
}
for (int i = 0; i < sub_range; i++) {
if (class_grd[i] == 0) {
class_grd[i] = NULL;
}
}
// return the pointer of pointer array.
return class_grd;
so on arr1 my code is working,
but on the arr2 it isn't.
i see that when i take the 45 or 46 grade and divine it in the range i'm inserting it to the 3 index (and it should be the second).
When I'm running the code through the school test checker i get this error:
timeout: the monitored command dumped core ../runTrain.sh: line 1: 21344 Segmentation fault timeout -k 5s 5s ./MainTrain.exe > ./mainTrain.log

Related

Compilation error - Array index is past the end of array blah.groupsToTrace[LENGTH_OF_A_MACRO] = ToTrace[number];

#include <stdio.h>
#define LENGTH_OF_A_MACRO 3
struct blahS
{
unsigned int groupsToTrace[LENGTH_OF_A_MACRO];
} blahS;
int main()
{
//Value hardcoded to 7 just for testing purpose. Otherwise value is assigned from another function
signed int trace = 7; //trace reads value of range [0-7] from a function
unsigned int ToTrace[LENGTH_OF_A_MACRO];
unsigned int number = 0;
unsigned int noOfGroups = 100;
if (trace != 0)
{
if ((trace == 1)) //b'001
{
ToTrace[number] = noOfGroups / 8;
number++;
}
if ((trace == 4)) //b'100
{
ToTrace[number] = noOfGroups / 2;
number++;
}
if ((trace == 7)) //b'111
{
ToTrace[number] = noOfGroups * 7 / 8;
number++;
}
}
struct blahS blah;
blah.groupsToTrace[LENGTH_OF_A_MACRO] = ToTrace[number]; //Compilation-error
return 0;
}
Basically this is an if-loop which checks and decides groupToTrace based on bit-mapping for a trace value allocated. At the last line i got a compilation error saying - Array index 3 is past the end of the array. I am assigning the calculated groupToTrace values to newPointer_p->groupsToTrace[3] where groupsToTrace[3] is stored in a struct
Question is I got a compilation error as mentioned above with out of bounds access to the array as i understand. But i don't understand where is the mistake.
Compiler version gcc.x86_64 4.8.5-39.el7 #GSS-RHEL7
Any clues or hints highly appreciated. Thanks in Advance!
You seem to misunderstand what this line is doing:
blah.groupsToTrace[LENGTH_OF_A_MACRO] = ToTrace[number];
This is not copying the entire contents of one array to another. It is copying index number of ToTrace to index LENGTH_OF_A_MACRO of blah.groupsToTrace. Both of these indices have the value 3 which is out of bounds for both arrays, as an array of size 3 has indices 0, 1, and 2.
You either need a loop to copy the elements:
int i;
for (i=0; i<number; i++) {
blah.groupsToTrace[i] = ToTrace[i];
}
Or you could use memcpy:
memcpy(blah.groupsToTrace, ToTrace, sizeof(blah.groupsToTrace));

Why is the pointer to pointer arithmatic failing in my conditional statment?

sm is a 2D array of character pointers allocated dynamically. I need to understand why my pointer to pointer arithmetic is failing in conditional if in loop structure.
2nd column in sm is where the string is that I need to test with the grade key gk which is array of characters/string. s holds row size and q is column size for 2D array, hm is my heap memory counter for freeing function which is not importing for my question.
double *cals(char **sm, char *gk, int s, int q, unsigned *hm) {
int c = 0;
double *savg = malloc(s * sizeof(double));
assert(savg);
*hm += 1;
for (int i = 0; i < s; *(savg + i) = c / q * 100 , c = 0, ++i) {
for (int j = 0; j < q; ++j) {
if (*(*(sm + i * STUDENTATT + 1) + j) == *(gk + j))
++c;
}
}
return savg;
}
There isn't much information given about the purpose of cals function so I had to make a few assumptions to write this answer.
Assumption-1(meaningful):-
You want to find how much characters in the two strings are equal(no every characters) and then find the percentage of the same characters to the total characters. If that is the case use the below code.
double *cals(char **sm, char *gk, int s, int q, unsigned *hm) {
float c = 0; // To force float division the c is declared as a float variable
double *savg = malloc(s * sizeof(double));
assert(savg);
*hm += 1;
char* sm_i_key = NULL;
unsigned int strlen_gk = strlen(gk);
unsigned int key_length = string_gk;
for (int i=0; i<s; ++i) { //The calculation is moved inside for loop
sm_i_key = *(sm+i*q+1); // You can also use sm_i_key = &sm[i*q+1]
/* Uncomment this section if length of 2 strings are not bound to be equal
if(strlen(sm_i_key) < strlen_gk){
key_length = sm_i_key;
}
else{
key_length = strlen_gk
}
*/
for (int j = 0; j < key_length; ++j) {
if (sm_i_key[j] == gk[j])
++c;
}
savg [i] = c / strlen_gk * 100; /* Since gk is the grade key it is assumed
to be equal to the total number.*/
c = 0;
}
return savg;
}
Assumption-2:-
You want to check whether the strings whose starting address is stored in the second column of each row of a 2D array sm is equal to the string stored in array pointed by gk and then calculate a value(double).
The function cals only returns 0.0 or 100.0 as the formula avgs[i]=c / q * 100 will only produce 0 if stings are not equal(since integer division c/q will always result in 0 if c is less than q which is the case here) and 100 if strings are equal(Then why use a double to store the value if only 0 and 100 is stored).
If that is the case then what you are doing here is fine unless the array gk and array sm[i][2] have different string length(not q). It would be better to use strncmp to check the equality of string if the string length of two array's are bound to be different.
Use the below code to do that:-
double *cals(char **sm, char *gk, int s, int q, unsigned *hm) {
int c;
char* sm_i_key = NULL;
double *savg = malloc(s * sizeof(double));
assert(savg);
*hm += 1;
for (int i=0; i < s;++i){//The calculation is moved to a static assignment given below
if(strncmp(sm_i_key, gk, strlen(gk) == 0)
{
savg[i] = 100.0; // Since c/q * 100 => 100.0 if q == c
}
else
{
savg[i] = 0.0; /*Since c/q *100 => 0.0 if q < c since integer
division will result in 0.*/
}
}
return savg;
}
I hope it helps.

C - creating a struct with input-dependent number of arrays

I’m wondering if anyone could provide any advice on storing “class slices” of values? I.e., for a table of values (see below for example), I want to be able to store, for each row, an array of the values that corresponds to each class. I have already sorted the table by class and determined the number of classes and the number of elements in each class. I’ve written out the basic conditions that I think will let me iterate over each row, over each class with the goal of copying the values from one struct into the new “class slice arrays”:
int x = 0;
for(int i = 0; i < total_rows - 1; i++)
{
for (int j = 0; j < n_classes; j++)
{
for (int k = 0; k < class_size[j]; k++)
{
data[x].value[i] = // value at the xth position of the “class slice” array for the jth class of values in the ith row //
x++;
}
}
}
where data[x].value[0] is the value I have stored for the xth sample in row i.
I guess my question is - how would you start mallocing space for arrays to store these class slices if you don’t know how many you’re even going to need (since that’s dependent on the number of rows and the number of classes)?
Example input, if this helps to make it clearer:
Class Case Case Case Case Case Case Case Case Case Case Case Case Case Case Control Control Control Control Control Control Control Control Control Control Control Control Control Control Control Control
Obs1 0.000741628 0.00308607 0.000267431 0.001418697 0.001237904 0.000761145 0.0008281 0.002426075 0.000236698 0.004924871 0.000722752 0.003758006 0.000104813 0.000986619 0.000121803 0.000666854 0 0.000171394 0.000877993 0.002717391 0.001336501 0.000812089 0.001448743 5.28E-05 0.001944298 0.000292529 0.000469631 0.001674047 0.000651526 0.000336615
Obs2 0.102002396 0.108035127 0.015052531 0.079923731 0.020643362 0.086480609 0.017907667 0.016279315 0.076263965 0.034876124 0.187481931 0.090615572 0.037460171 0.143326961 0.029628502 0.049487575 0.020175439 0.122975405 0.019754837 0.006702899 0.014033264 0.040024363 0.076610375 0.069287599 0.098896479 0.011813681 0.293331246 0.037558052 0.303052867 0.137591517
Obs3 0.218495065 0.242891829 0.23747851 0.101306336 0.309040188 0.237477347 0.293837554 0.34351816 0.217572429 0.168651691 0.179387106 0.166516699 0.099970652 0.181003474 0.076126675 0.10244981 0.449561404 0.139257863 0.127579104 0.355797101 0.354544105 0.262855651 0.10167146 0.186068602 0.316763006 0.187466247 0.05701315 0.123825467 0.064780343 0.069847682
Obs4 0.141137543 0.090948286 0.102502388 0.013063365 0.162060849 0.166292135 0.070215996 0.063535037 0.333743609 0.131011609 0.140936687 0.150108506 0.07812762 0.230704405 0.069792935 0.120770743 0.164473684 0.448110378 0.42599534 0.074094203 0.096525097 0.157661185 0.036737518 0.213931398 0.091119285 0.438073807 0.224921728 0.187034237 0.06611442 0.086005218
Obs5 0.003594044 0.003948354 0.008137536 0.001327901 0.002161974 0.003552012 0.002760334 0.001898667 0.001420186 0.003165988 0.001011853 0.001217382 0.000314439 0.004254794 0.000213155 0.003650147 0 0.002742309 0.002633978 0 0.002524503 0.002146234 0.001751465 0.006543536 0.003941146 0.00049505 0.00435191 0.001944054 0.001303053 0.004207692
Obs6 0.000285242 2.27E-05 0 1.13E-05 0.0002964 3.62E-05 0.000138017 0.000210963 0.000662753 0 0 0 0 4.11E-05 0 0 0 0 0.000101307 0 0 0 0 5.28E-05 0.00152391 0 0 0 0 0
Obs7 0.002624223 0.001134584 0.00095511 0.000419934 0.000401011 0.001739761 0.00272583 0.002566717 0.000520735 0.002311674 0.006287944 0 6.29E-05 0.000143882 3.05E-05 0.000491366 0 0 3.38E-05 0 0.001782002 0.000957104 0.002594763 0.000527704 0.000105097 0.001192619 3.13E-05 0 0.000744602 0.000252461
Obs8 0.392777683 0.383875286 0.451499522 0.684663315 0.387394299 0.357992026 0.488406597 0.423473155 0.27267563 0.47454646 0.331020526 0.484041709 0.735955056 0.338841956 0.781699147 0.625403622 0.313596491 0.270545891 0.379259109 0.498913043 0.372438372 0.446271644 0.606698813 0.305593668 0.360535996 0.29889739 0.328710081 0.521222594 0.419924299 0.584111756
I think what you are looking for is a struct containing a pointer to double and an int that keeps track of the number of values being stored:
struct data {
int numValues;
double *value;
};
If you don't know how many data structs you need, you declare a pointer to one, and allocate them as needed. Whenever you allocate a new data struct, you set the value to point to NULL. As you add values, you reallocate storage, reset the value pointer, and update the counter for that struct. Here is a little toy example to illustrate how it works:
#include <stdio.h>
#include <stdlib.h>
struct data {
int numValues;
double *value;
};
int main(void)
{
struct data *myData;
int numData = 0;
int i, last;
myData = NULL;
++numData;
i = numData - 1;
myData = realloc(myData, sizeof(*myData) * numData);
myData[i].numValues = 0;
myData[i].value = NULL;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 3.1415926536;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 2.7182818285;
printf("Value [0][0]: %lf\n", myData[i].value[0]);
printf("Value [0][1]: %lf\n", myData[i].value[1]);
++numData;
i = numData - 1;
myData = realloc(myData, sizeof(*myData) * numData);
myData[i].numValues = 0;
myData[i].value = NULL;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 1.6180340;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 2.99792458;
printf("Value [1][0]: %lf\n", myData[i].value[0]);
printf("Value [1][1]: %lf\n", myData[i].value[1]);
/* Let's free all of the memory we allocated! */
// first, free space allocated for values
for (int j = 0; j <= i; j++)
free(myData[j].value);
// next, free space allocated for structs
free(myData);
return 0;
}
Edit
I cleaned up the code a bit and stored a couple of numbers in a second struct in an attempt to make the example a little more illustrative. I also added a few lines to free the allocated memory.
I am not exactly sure what your data is representing, but it might be useful to add a second dynamic array to the struct:
struct data {
int numCase;
int numControl;
double *caseValue;
double *controlValue;
};
Some variation on this idea would allow you to keep your "case" and "control" values separate, and you could manage the addition of new values as before.

Implement Array Random Shuffle

Prelude
I am writing a grid-based random-map generator.
Currently, I want to populate a 2D array with a variety of tiles.
Problem
In the parenthesis is a more concrete example.
Here is what you are given:
2D array and its dimensions. (i.e. 3x4 grid)
Integer Random(Range) (i.e. Range: 0-11, Output: integer from 0-11)
You do NOT have a function that randomly sorts an array, unless you implement it yourself.
Number of each type of tile (i.e. Desert: 2, Lake: 4, Forrest: 6)
How do I populate this array with the given tiles?
Example
3x4 map; 6 Forrest; 4 Lake; 2 Desert...
F F L
L D F
D F F
L F L
Attempt
I do have my own implementation, however its Big-O is... infinity, I think. :)
Of course, the chances that it will never finish are slim; however, this is part of a video game and I don't want to keep the player waiting.
Postscript
I don't really care what language that it is implemented in; pseudo-code will be satisfactory.
make sure the inputs are correct (e.g. total count of tiles equals
the count of slots in the grid)
put all give tiles into a queue Q, whose length is n(in your
example, n=12)
intialize a result array R[p], p is intialized as 0
get k=random(1->n), deque Q[k] into R[p], p++
repeat step 4 until p goes to n
Things can be much more easier if you use a language that has built-in sort functions:
verify input
put given tiles into an one demension array A[n]
sort A[n] randomly
Code in C#:
int m = 3;
int n = 4; //m*n grid
int forrests = 6;
int lakes = 4;
int deserts = 2;
if (m * n != forrests + lakes + deserts)
{
//invalid input!
}
char[] tiles = new char[m * n];
for (int i = 0; i < m * n; i++)
{
if (i < forrests)
{
tiles[i] = 'F';
}
else if (i < forrests + lakes)
{
tiles[i] = 'L';
}
else
{
tiles[i] = 'D';
}
}
//preparation completed, now tiles[] looks like
//F,F,F,F,F,F,L,L,L,L,D,D
char[] output = tiles.OrderBy(t => Guid.NewGuid()).ToArray();
//output is randomly sorted from tiles
//if you really need a two-demension array
char[][] map = new char[n][];
for (int i = 0; i < n; i++)
{
map[i] = output.Skip(m * i).Take(m).ToArray();
}
This could one of the way to do it.
#include<iostream>
#include <cstdlib>
#include <map>
using namespace std;
//Map which keeps the value for each key (2,4,6)
map<int,char> alphabet;
void initMap()
{
alphabet[2] = 'D';
alphabet[4] = 'L';
alphabet[6] = 'F';
}
int main()
{
char a[3][4];
// counter variables to keep track of d,f and l
int temp,d=0,f=0,l=0;
initMap();
for(int i=0;i<3;i++)
{
for(int j=0;j<4;j++)
{
//This determines if the generated random number is already entered in the grid. If no than breaks out. If yes than again a new random number is generated and process is iterated untill the new number is found to enter
while(1)
{
temp = rand()%4;
if(temp==0)
{
temp = 2;
}
else
{
temp = temp*2;
}
if(temp ==2 && d<2)
{
d++;
break;
}
else if(temp ==4 && l<4)
{
l++;
break;
}
else if(temp ==6 && f<6)
{
f++;
break;
}
else
{
continue;
}
}
//char value for the number temp is assigned from the alphabet map
a[i][j] = alphabet.at(temp);
cout<<a[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
output:
D F L D
L L L F
F F F F
You can map the alphabet according the number when entering the value or accessing the value from array.

Using pointers in 2D arrays

I'm attempting to store arrays of integers that I read from a file (with a separate function) in a 2D array but I keep having issues with Segmentation fault. I know it's an issue with my pointers but I can't figure out exactly what I'm doing wrong.
Here is my function (takes an integer and compares it with an integer read from a file before storing it in my 2D array).
int **getStopTimes(int stop_id) {
int **result = malloc(sizeof(*result));
char const* const fileName = "stop_times_test.txt";
FILE* txt = fopen(fileName, "r");
char line[256];
int count = 0;
while (fgets(line, sizeof(line), txt) != NULL) {
int *formattedLine = getStopTimeData(line); //getStopTimeData returns a pointer to an array of ints, memory is allocated in the function
if (formattedLine[1] == stop_id) {
result[count] = formattedLine;
count++;
}
}
fclose(txt);
return result;
}
And my main:
int main(int argc, char *argv[]) {
int **niceRow = getStopTimes(21249);
for (int i=0; i<2; i++) { //Only looping 3 iterations for test purposes
printf("%d,%d,%d,%d\n",niceRow[i][0], niceRow[i][1], niceRow[i][2], niceRow[i][3]);
}
free(niceRow);
return 0;
}
getStopTimeData function thats being called (Pulls certain information from an array of chars and stores/returns them in an int array):
int *getStopTimeData(char line[]) {
int commas = 0;
int len = strlen(line);
int *stopTime = malloc(4 * sizeof(*stopTime)); //Block of memory for each integer
char trip_id[256]; //Temp array to build trip_id string
char stop_id[256]; //Temp array to build stop_id string
int arrival_time; //Temp array to build arrival_time string
int departure_time; //Temp array to build departure_time string
int counter;
for(int i = 0; i <len; i++) {
if(line[i] == ',') {
commas++;
counter = 0;
continue;
}
switch(commas) { //Build strings here and store them
case 0 :
trip_id[counter++] = line[i];
if(line[i+1] == ',') trip_id[counter] = '\0';
break;
case 1: //Convert to hours past midnight from 24hr time notation so it can be stored as int
if(line[i] == ':' && line[i+3] == ':') {
arrival_time = (line[i-2]-'0')*600 + (line[i-1]-'0')*60 + (line[i+1]-'0')*10 + (line[i+2]-'0');
}
break;
case 2 :
if(line[i] == ':' && line[i+3] == ':') {
departure_time = (line[i-2]-'0')*600 + (line[i-1]-'0')*60 + (line[i+1]-'0')*10 + (line[i+2]-'0');
}
break;
case 3 :
stop_id[counter++] = line[i];
if(line[i+1] == ',') stop_id[counter] = '\0';
break;
}
}
//Assign and convert to ints
stopTime[0] = atoi(trip_id);
stopTime[1] = atoi(stop_id);
stopTime[2] = arrival_time;
stopTime[3] = departure_time;
return stopTime;
}
This line:
int **result = malloc(sizeof(*result));
allocates just memory for one single pointer. (*result is of type int *, so it's a pointer to data -- the sizeof operator will tell you the size of a pointer to data ... e.g. 4 on a 32bit architecture)
What you want to do is not entirely clear to me without seeing the code for getStopTimeData() ... but you definitely need more memory. If this function indeed returns a pointer to some ints, and it handles allocation correctly, you probably want something along the lines of this:
int result_elements = 32;
int **result = malloc(sizeof(int *) * result_elements);
int count = 0;
[...]
if (formattedLine[1] == stop_id) {
if (count == result_elements)
{
result_elements *= 2;
result = realloc(result, result_elements);
}
result[count] = formattedLine;
count++;
}
Add proper error checking, malloc and realloc could return (void *)0 (aka null) on out of memory condition.
Also, the 32 for the initial allocation size is just a wild guess ... adapt it to your needs (so it doesn't waste a lot of memory, but will be enough for most use cases)
The upper answer is good,
just to give you an advice try to avoid using 2D array but use a simple array where you can store all your data, this ensures you to have coalescent memory.
After that, you can access your 1D array with an easy trick to see it like a 2D array
Consider that your 2D array has a line_size
To access it like a matrix or a 2d array you need to find out the corresponding index of your 1d array for given x,y values
index = x + y * line size;
In the opposite way:
you know the index, you want to find x and y corresponding to this index.
y = index / line_size;
x = index mod(line_size);
Of course, this "trick" can be used if you already know your line size

Resources